﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Collections.ObjectModel;

using Noris.Schedule.Support;
using Noris.Schedule.Support.Data;
using Noris.Schedule.Support.Sql;
using Noris.Schedule.Support.Core;
using Noris.Schedule.Support.Services;
using Noris.Schedule.Support.DataFace;
using Noris.Schedule.Planning.DataFace;

// Tento soubor obsahuje definice tříd pro evidenci detailů pracovních úkolů (WorkPass, WorkTime, WorkUnit) = strom úkolů jednoho tasku
namespace Noris.Schedule.Planning.ProcessData
{
	#region CLASS WorkPassCls : data jednoho celého paralelního průchodu. Jeden průchod = ITaskRegData pro registraci kapacit.
	/// <summary>
	/// WorkPassCls : data jednoho celého paralelního průchodu.
	/// Jeden paralelní průchod hraje roli zadání dat do kapacitního plánovacího procesu.
	/// </summary>
	public class WorkPassCls : PlanItemCls<CapacityPlanWorkItemCls>, ITaskRegData
	{
		#region KONSTRUKTORY
		/// <summary>
		/// Konstruktor = pro konkrétního Parenta, kterým je záznam úkolu
		/// </summary>
		/// <param name="parent"></param>
		private WorkPassCls(CapacityPlanWorkItemCls parent)
			: base(parent)
		{
			this.Init();
		}
		/// <summary>
		/// Konstruktor = pro konkrétního Parenta, kterým je záznam úkolu
		/// </summary>
		/// <param name="parent">Objekt parenta</param>
		/// <param name="withNoID">Požadavek na potlačení přidělení čísla ID: true = ID bude 0 a čítač ID se nezmění, false = objekt dostane normální platné ID</param>
		private WorkPassCls(CapacityPlanWorkItemCls parent, bool withNoID)
			: base(parent, withNoID)
		{
			this.Init();
		}
		/// <summary>
		/// Provede inicializaci dat objektu
		/// </summary>
		protected void Init()
		{
			if (this.ItemID != 0)
			{	// Pro běžný objekt se vytvoří vnitřní data:
				// (pokud ale je objekt vytvořen s parametrem withNoID == true, metodou CreateTemporaryPass(), 
				//  pak nemá svoje ID a nebude mít ani svoje data = pro jistotu, že nebude použit):
				this._WorkTimeLst = new List<WorkTimeCls>();
                this._UnitList = new List<TaskChangeOneSourceUnitCls>();
            }
            this._WorkTimeListUnsorted = false;
		}
		/// <summary>
		/// Vytvoří a vrátí objekt pro jeden paralelní průchod na základě tasku (parent).
		/// </summary>
		/// <param name="parent">Data o úkolu = task (operace)</param>
        /// <param name="qtyRequired">Množství požadované do výroby v tomto průběhu, na toto množství se bude počítat TAC</param>
        /// <param name="qtyFinal">Celkové množství vyrobené tímto průběhem (včetně již vyrobených kusů), ovlivní určení kapacit pro nevýrobní fáze</param>
		/// <param name="unitCount">Množství vyrobené jedním cyklem TAC v tomto průchodu (může se lišit od UnitCount na operaci)</param>
		/// <returns>Prázdný paralelní průchod s nastaveným množstvím</returns>
        internal static WorkPassCls Create(CapacityPlanWorkItemCls parent, decimal qtyRequired, decimal qtyFinal, decimal unitCount)
		{
			WorkPassCls pass = new WorkPassCls(parent);
			pass.SetPassQty(qtyFinal, qtyRequired, unitCount);
            pass.CloneSourceFromParent();
			return pass;
		}
		/// <summary>
		/// Vytvoří a vrátí objekt pro jeden paralelní průchod na základě tasku (parent).
		/// Vytvořený objekt nemá ID a nesmí se v systému používat, typicky slouží jen pro výpočty časů tasku, ale ne k evidenci dat.
		/// Objekt pak úmyslně nemá vytvořené interní objekty _WorkTimeLst, _UnitList aby jej nebylo možno použít pro běžnou práci.
		/// </summary>
		/// <param name="parent">Data o úkolu = task (operace)</param>
		/// <param name="qtyFinal">Celkové množství vyrobené tímto průběhem (včetně již vyrobených kusů), ovlivní určení kapacit pro nevýrobní fáze</param>
		/// <param name="qtyRequired">Množství požadované do výroby v tomto průběhu, na toto množství se bude počítat TAC</param>
		/// <param name="unitCount">Množství vyrobené jedním cyklem TAC v tomto průchodu (může se lišit od UnitCount na operaci)</param>
		/// <returns>Prázdný paralelní průchod s nastaveným množstvím</returns>
		internal static WorkPassCls CreateTemporaryPass(CapacityPlanWorkItemCls parent, decimal qtyFinal, decimal qtyRequired, decimal unitCount)
		{
			WorkPassCls pass = new WorkPassCls(parent, true);
			pass.SetPassQty(qtyFinal, qtyRequired, unitCount);
            pass.CloneSourceFromParent();
			return pass;
		}
		/// <summary>
		/// Vizualizace
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			return "{Pass: QtyRequired=" + this.QtyRequired + "; Time=" + this.TimeWork + "; TimeCnt=" + this.WorkTimeLst.Count + "}";
		}
		#endregion
		#region DATOVÉ PROMĚNNÉ

		// Pass:
		/// <summary>
		/// Pracovní položka WorkTime, která se právě registruje (postupně se do ní přidávají položky WorkUnit).
		/// </summary>
		private WorkTimeCls _WorkTimeCurrent;
		/// <summary>
		/// Seznam položek WorkTime, standardní
		/// </summary>
		private List<WorkTimeCls> _WorkTimeLst;
		/// <summary>
		/// Soupis kapacitních jednotek, které mají výjimku ze standardního plánovacího algoritmu.
		/// </summary>
		private List<TaskChangeOneSourceUnitCls> _UnitList;
		/// <summary>
		/// Příznak, že soupis časů _WorkTimeList není setříděn (true) / je prázdný nebo je setříděn (false)
		/// </summary>
		private bool _WorkTimeListUnsorted;

		// ITaskRegData:
		private decimal _UnitCountRequired;
		private decimal _UnitCount;
		private decimal _QtyFinal;
		private decimal _QtyRequired;
        private List<ITaskSourceData> _SourceList;
		private DateTime _StartTime;
		private OperationPhase _StartPhase;
		private CapacityRegisterState _RegisterState;

		private DateTime _TMcBeg;
		private DateTime _TMcEnd;
		private decimal _TMcMis;
		private DateTime _TBcBeg;
		private DateTime _TBcEnd;
		private decimal _TBcMis;
		private DateTime _TAcBeg;
		private DateTime _TAcEnd;
		private decimal _TAcMis;
		private DateTime _TEcBeg;
		private DateTime _TEcEnd;
		private decimal _TEcMis;
		private DateTime _WorkBegin;
		private DateTime _WorkEnd;
		private TimeRange.TimeDirection _TimeDir;

		// ITaskTimeData:
		private decimal _TMcReq;
		private decimal _TBcReq;
		private decimal _TAcReq;
		private decimal _TEcReq;
		private decimal _TAcQty;
		private decimal _TMcSec;
		private decimal _TAcSec;
		private bool _TAcDiv;

		#endregion
		#region PROPERTY
		/// <summary>
		/// Veřejný (public) read-only souhrn pracovních časů. Vždy je setříděn podle času Begin (a podle fáze M-B-A-E).
		/// Do tohoto listu není možno přidávat / odebírat položky. Je to ReadOnlyCollection.
		/// Je možno ji používat jako normální List, vyjma jakýchkoli modifikací.
		/// </summary>
		public ReadOnlyCollection<WorkTimeCls> WorkTimeList { get { this._WorkTimeListSortIfNeed(); return new ReadOnlyCollection<WorkTimeCls>(this._WorkTimeLst); } }
		/// <summary>
		/// Interní souhrn pracovních časů. Přímá reference na privátní List (rychlá).
		/// Vždy je setříděn podle času Begin (a podle fáze M-B-A-E)
		/// </summary>
		internal List<WorkTimeCls> WorkTimeLst { get { this._WorkTimeListSortIfNeed(); return this._WorkTimeLst; } }
		/// <summary>
		/// Časový interval celého průchodu. Nelze setovat, je to struktura. Hodnoty se vkládají do this.WorkBegin, this.WorkEnd.
		/// </summary>
		internal TimeRange TimeWork { get { return new TimeRange(this.WorkBegin, this.WorkEnd); } }
		/// <summary>
		/// Časový interval fáze MoveTime. Nelze setovat.
		/// </summary>
		internal TimeRange TimeTMC { get { return new TimeRange(this.TMcBeg, this.TMcEnd); } }
		/// <summary>
		/// Časový interval fáze MoveTBC. Nelze setovat.
		/// </summary>
		internal TimeRange TimeTBC { get { return new TimeRange(this.TBcBeg, this.TBcEnd); } }
		/// <summary>
		/// Časový interval fáze MoveTAC. Nelze setovat.
		/// </summary>
		internal TimeRange TimeTAC { get { return new TimeRange(this.TAcBeg, this.TAcEnd); } }
		/// <summary>
		/// Časový interval fáze MoveTEC. Nelze setovat.
		/// </summary>
		internal TimeRange TimeTEC { get { return new TimeRange(this.TEcBeg, this.TEcEnd); } }
		#endregion
		#region IMPLEMENTACE INTERFACE

		// ITaskRegData:
		/// <summary> { get; } Klíč záznamu na S ose, pro který je tento úkol prováděn. Jedna S osa může mít více operací = Task.</summary>
		public int AxisID { get { return this.Parent.AxisID; } }
		/// <summary> { get; } Data osy S = materiálová osa</summary>
		internal MaterialPlanAxisItemCls AxisData { get { return this.Parent.AxisData; } }
		/// <summary> { get; } Klíč úkolu = operace. Jedna operace může mít 1 i víc průchodů = Pass.</summary>
		public int TaskID { get { return this.Parent.TaskID; } }
		/// <summary> { get; } Číslo DB záznamu tasku = operace</summary>
		public int TaskRecordNumber { get { return this.Parent.RecordNumber; } }
		/// <summary> { get; } Data úkolu = operace. Jedna operace může mít 1 i víc průchodů = Pass.</summary>
		public CapacityPlanWorkItemCls TaskData { get { return this.Parent; } }
		/// <summary> { get; } GID průchodu = klíč z čísla pracovní třídy (Constants.ClassNumberPass = 0x10002) a čísla záznamu PassID</summary>
		public GID PassGID { get { return new GID(Constants.ClassNumberPass, this.ItemID); } }
		/// <summary> { get; } Klíč průchodu = jeden kontinuální průběh operace výrobou. Pokud je jedna operace rozdělena na více paralelních průchodů, má každý svoje PassID.</summary>
		public int PassID { get { return this.ItemID; } }
		/// <summary> { get; } Druh tasku, určuje cílový druh čerpaných kapacit (Reál, Plán, Simulace)</summary>
		public CapacityUsageType CapacType { get { return this.Parent.CapacType; } }
		/// <summary> { get; } Příznak, zda operace je časově fixovaná (true = fixovaná / false = volná)</summary>
		internal bool IsFixedTask { get { return this.Parent.IsFixedTask; } set { this.Parent.IsFixedTask = value; } }
		/// <summary> { get; } Druh omezení kapacit, vychází z parametrů funkce Zaplánování kapacit. (Omezené, Neomezené, Podle KPJ)</summary>
		public LimitedCType LimitType { get { return this.Parent.LimitType; } }
		/// <summary> { get; } Způsob alokace času MoveTime. Defaultně nebývá nastavena hodnota "RegisterIntoUsedPlanUnits" (ale může být). Při zadání hodnoty AllocMoveTimeMode = CalculateTimeByCalendar se má zadat explicitní kalendář do CalendarForMoveTime.</summary>
		public AllocMode AllocMoveTimeMode { get { return this.Parent.AllocMoveTimeMode; } }
		/// <summary> { get; } Explicitní kalendář, použitý pro čas MoveTime při režimu : AllocMoveTimeMode = CalculateTimeByCalendar.</summary>
		public int CalendarForMoveTime { get { return this.Parent.CalendarForMoveTime; } }
		/// <summary> { get; } Způsob alokace času těch fází, kterých se neúčastní žádná KPJ (žádná nemá nastaveno Plánovat anebo Použít čas fáze). Při zadání hodnoty AllocNotPlanUnitMode = CalculateTimeByCalendar se má zadat explicitní kalendář do CalendarForNotPlanUnit.</summary>
		public AllocMode AllocNotPlanUnitMode { get { return this.Parent.AllocNotPlanUnitMode; } }
		/// <summary> { get; } Explicitní kalendář, použitý pro fáze bez KPJ, při režimu : AllocNotPlanUnitMode = CalculateTimeByCalendar.</summary>
		public int CalendarForNotPlanUnit { get { return this.Parent.CalendarForNotPlanUnit; } }
		/// <summary> { get; } Nejvyšší počet současných průchodů touto operací</summary>
		public decimal ParalelPassingCount { get { return this.Parent.ParalelPassingCount; } }
		/// <summary>Počet najednou vyrobených kusů, požadavek (může snížen podle disponibilních kapacit, viz UnitCount)</summary>
		public decimal UnitCountRequired { get { return this._UnitCountRequired; } }
		/// <summary>Počet najednou vyrobených kusů, realita (může být menší, než UnitCountRequired)</summary>
		public decimal UnitCount { get { return this._UnitCount; } set { this._UnitCount = value; } }
		/// <summary> { get; } Transportní dávka. Ovlivňuje výpočet času odkladu dávky i výpočet jednotkového času.</summary>
		public decimal MoveBatchQty { get { return this.Parent.MoveBatchQty; } }
		///<summary> { get; } Množství finálního výrobku celkem - toto množství se neplánuje, jen slouží k určování potřebnosti alokace fáze MoveTime, TBC, TEC.</summary>
		public decimal QtyFinal { get { return this._QtyFinal; } }
		///<summary> { get; } Množství chybějící, toto se bere do plánu. Fyzicky vyráběný počet na tomto tasku.</summary>
		public decimal QtyRequired { get { return this._QtyRequired; } }
		/// <summary> { get; } Okamžik počátku registrace kapacit (výchozí FlowTime). Směr je dáv v TimeDir.</summary>
		public DateTime StartTime { get { return this._StartTime; } }
		/// <summary> { get; } První fáze, která se bude alokovat. Umožní alokovat počínaje od fáze TAC (pokud plánujeme koordinaci výrobních dávek.</summary>
		public OperationPhase StartPhase { get { return this._StartPhase; } }
		/// <summary>Stav registrace kapacit, výsledné informace.</summary>
		public CapacityRegisterState RegisterState { get { return this._RegisterState; } set { this._RegisterState = (value == CapacityRegisterState.Initialized ? value : (this._RegisterState | value)); } }
		/// <summary> { get; set; } Datum a čas počátku fáze MOVE_TIME (adjust) (=výsledek do task.move_time_start_adj)</summary>
		public DateTime TMcBeg { get { return this._TMcBeg; } set { this._TMcBeg = value; } }
		/// <summary> { get; set; } Datum a čas konce fáze MOVE_TIME</summary>
		public DateTime TMcEnd { get { return this._TMcEnd; } set { this._TMcEnd = value; } }
		/// <summary>Chybějící kapacita pro fázi MOVE_TIME</summary>
		public decimal TMcMis { get { return this._TMcMis; } set { this._TMcMis = value; } }
		/// <summary> { get; set; } Datum a čas počátku fáze TBC (adjust)</summary>
		public DateTime TBcBeg { get { return this._TBcBeg; } set { this._TBcBeg = value; } }
		/// <summary> { get; set; } Datum a čas konce fáze TBC</summary>
		public DateTime TBcEnd { get { return this._TBcEnd; } set { this._TBcEnd = value; } }
		/// <summary>Chybějící kapacita pro fázi TBC</summary>
		public decimal TBcMis { get { return this._TBcMis; } set { this._TBcMis = value; } }
		/// <summary> { get; set; } Datum a čas počátku fáze TAC</summary>
		public DateTime TAcBeg { get { return this._TAcBeg; } set { this._TAcBeg = value; } }
		/// <summary> { get; set; } Datum a čas konce fáze TAC</summary>
		public DateTime TAcEnd { get { return this._TAcEnd; } set { this._TAcEnd = value; } }
		/// <summary>Chybějící kapacita pro fázi TAC</summary>
		public decimal TAcMis { get { return this._TAcMis; } set { this._TAcMis = value; } }
		/// <summary> { get; set; } Datum a čas počátku fáze TEC</summary>
		public DateTime TEcBeg { get { return this._TEcBeg; } set { this._TEcBeg = value; } }
		/// <summary> { get; set; } Datum a čas konce fáze TEC = konec celého úkolu</summary>
		public DateTime TEcEnd { get { return this._TEcEnd; } set { this._TEcEnd = value; } }
		/// <summary>Chybějící kapacita pro fázi TEC</summary>
		public decimal TEcMis { get { return this._TEcMis; } set { this._TEcMis = value; } }
		/// <summary> { get; set; } Datum a čas počátku výroby. Je určováno až po dokončení registrace kapacit tasku.</summary>
		public DateTime WorkBegin { get { return this._WorkBegin; } set { this._WorkBegin = value; } }
		/// <summary> { get; set; } Datum a čas konce výroby. Je určováno až po dokončení registrace kapacit tasku.</summary>
		public DateTime WorkEnd { get { return this._WorkEnd; } set { this._WorkEnd = value; } }
		/// <summary> { get; } Časový interval celého průchodu = čas od WorkBegin do WorkEnd. Nelze setovat, ani do vráceného objektu. Je to struktura, vytvořená v get{}.</summary>
		public TimeRange WorkTimeRange { get { return new TimeRange(this._WorkBegin, this._WorkEnd); } }
		/// <summary> { get; set; } 
		/// Směr alokace kapacit. Platí pro fázi StartPhase, a fáze po ní následující.
		/// Pokud alokace kapacit bude probíhat "odprostředka" (typicky od TAC), pak jde o směr dané fáze a fází po ní. Doplňkové fáze budou alokovány reverzně.
		/// Při směru do budoucna (ToFuture) se alokují kapacity počínaje od WorkBegin, a výsledný čas se ukládá do WorkEnd.
		/// Při směru do minulosti (ToHistory) se alokují kapacity počínaje od WorkEnd, a výsledný čas se ukládá do WorkBegin.
		/// </summary>
		public TimeRange.TimeDirection TimeDir { get { return this._TimeDir; } set { this._TimeDir = value; } }
		/// <summary> { get; set; } Soupis zdrojů kapacit, které se účastní práce. Konkrétní kapacitní plánovací jednotky budou dohledány později.</summary>
        public List<ITaskSourceData> SourceList 
        {
            get 
            { 
                if (this._SourceList == null || (this._SourceList.Count == 0 && this.Parent.SourceList != null && this.Parent.SourceList.Count > 0))
                    this.CloneSourceFromParent();
                return this._SourceList;
            }
            set { this._SourceList = value; }
        }
		/// <summary>Soupis kapacitních jednotek, které mají výjimku ze standardního plánovacího algoritmu.</summary>
		public List<TaskChangeOneSourceUnitCls> UnitList { get { return this._UnitList; } }

		// ITaskTimeData:
		/// <summary> { get; set; } Kapacita požadovaná na fázi MOVE_TIME, v sekundách</summary>
		public decimal TMcReq { get { return this._TMcReq; } set { this._TMcReq = value; } }
		/// <summary> { get; set; } Kapacita požadovaná na fázi BEGIN, v sekundách</summary>
		public decimal TBcReq { get { return this._TBcReq; } set { this._TBcReq = value; } }
		/// <summary> { get; set; } Kapacita požadovaná na fázi A, v sekundách. Celková kapacita na celý task.</summary>
		public decimal TAcReq { get { return this._TAcReq; } set { this._TAcReq = value; } }
		/// <summary> { get; set; } Kapacita požadovaná na fázi END, v sekundách</summary>
		public decimal TEcReq { get { return this._TEcReq; } set { this._TEcReq = value; } }
		/// <summary> { get; set; } Počet dávek (cyklů) fáze A. Odpovídá vyráběnému množství (případně děleno UnitCount, zaokrouhleno na celá čísla nahoru).</summary>
		public decimal TAcQty { get { return this._TAcQty; } set { this._TAcQty = value; } }
		/// <summary>Kapacita fáze MOVE_TIME, v sekundách, bez ohledu na stav výroby. Je rovno MOVE_TIME * MOVE_TIME_MU.</summary>
		public decimal TMcSec { get { return this._TMcSec; } set { this._TMcSec = value; } }
		/// <summary>Kapacita jednotková pro fázi A, v sekundách. Jde o čas na výrobu jednoho počtu UnitCount. Je rovno TAC * TAC_MU.</summary>
		public decimal TAcSec { get { return this._TAcSec; } set { this._TAcSec = value; } }
		/// <summary> { get; set; } Příznak dělitelnosti množství na zlomky. Podle tohoto příznaku se bude zacházet s časy menšími, než TAcSec: pro nedělitelné operace bude čas kratší než TAcSec nepoužitelný (nevyrobím ani jeden kus). pro dělitelné operace jej využiju (vyrobím alespoň 0.028 litru).</summary>
		public bool TAcDiv { get { return this._TAcDiv; } set { this._TAcDiv = value; } }

		#endregion
		#region METODY ITaskRegData : PŘIDÁNÍ ČASU, PŘIDÁNÍ PUC
		/// <summary>
		/// Zajistí přidání dat o novém časovém úseku, který byl přidělen na práci
		/// </summary>
		/// <param name="args">Data o přidávaném čase</param>
        public void AddTime(TaskRegDataAddTimeArgs args)
        {
            this.AddTime(args, false);
        }
		/// <summary>
		/// Zajistí přidání dat o novém časovém úseku, který byl přidělen na práci
		/// </summary>
		/// <param name="args">Data o přidávaném čase</param>
        /// <param name="saveResult">Uložit i do proměnných časů</param>
		public void AddTime(TaskRegDataAddTimeArgs args, bool saveResult)
		{
			if (args != null)
			{	// Zadaný nový čas:
				WorkTimeCls time = WorkTimeCls.Create(this, args);
				this._WorkTimeCurrent = time;
				if (!this._TimeRegisterActive)
				{
					this._WorkTimeLst.Add(time);
					this._WorkTimeListUnsorted = true;            // Příznak, že soupis časů _WorkTimeList není setříděn
				}
				else
				{
					this._WorkTimeInsertLst.Add(time);
				}
                if (saveResult)
                    this._SaveTimeResult(time);
			}
			else
			{	// Ukončení času:
				this._WorkTimeCurrent = null;
			}
		}
        /// <summary>
        /// Do svých proměnných (TMcBeg, TMcEnd, atd pro další fáze) upraví hodnoty času podle času dodaného v parametru.
        /// Časy zdejší pouze prodlužuje (rozšiřuje). Neřeší kolize mezi fázemi.
        /// </summary>
        /// <param name="time"></param>
        private void _SaveTimeResult(WorkTimeCls time)
        {
            TimeRange workTime = time.WorkTime;
            switch (time.Phase)
            {
                case OperationPhase.MoveTime:
                    this._TMcBeg = _SaveTimeResultBegin(this._TMcBeg, workTime.Begin);
                    this._TMcEnd = _SaveTimeResultEnd(this._TMcEnd, workTime.End);
                    break;
                case OperationPhase.TBC:
                    this._TBcBeg = _SaveTimeResultBegin(this._TBcBeg, workTime.Begin);
                    this._TBcEnd = _SaveTimeResultEnd(this._TBcEnd, workTime.End);
                    break;
                case OperationPhase.TAC:
                    this._TAcBeg = _SaveTimeResultBegin(this._TAcBeg, workTime.Begin);
                    this._TAcEnd = _SaveTimeResultEnd(this._TAcEnd, workTime.End);
                    break;
                case OperationPhase.TEC:
                    this._TEcBeg = _SaveTimeResultBegin(this._TEcBeg, workTime.Begin);
                    this._TEcEnd = _SaveTimeResultEnd(this._TEcEnd, workTime.End);
                    break;
            }
        }
        private DateTime _SaveTimeResultBegin(DateTime current, DateTime work)
        {
            return (current == DateTime.MinValue || work < current ? work : current);
        }
        private DateTime _SaveTimeResultEnd(DateTime current, DateTime work)
        {
            return (current == DateTime.MinValue || work > current ? work : current);
        }
		/// <summary>
		/// Zajistí přidání dat o nově určené kapacitní jednotce, která byla přidělena na práci
		/// </summary>
		/// <param name="args"></param>
		public void AddUnit(TaskRegDataAddUnitArgs args)
		{
			if (this._WorkTimeCurrent == null)
				Throw.SysError(MessageInfo.Get("Chyba řízení: registrace kapacit předává informaci o Unit, ale nepředala informaci o Time."));

			// Vytvořit pracovní jednotku a vložit do aktuálního času:
			WorkUnitCls workUnit = WorkUnitCls.Create(this._WorkTimeCurrent, args);
			this._WorkTimeCurrent.WorkUnitLst.Add(workUnit);

			// Vyjádřit přání o registraci kapacity pro práci:
			args.RegisterJob = true;
			args.WorkUnit = workUnit;
		}
		/// <summary>
		/// Metoda je vyvolaná vždy na konci registrace kapacit.
		/// Účel: například setřídit data o registrovaných kapacitách. 
		/// Kapacity (jejich časy) jsou registrovány v pořadí, které je vhodné pro plánování, ale neodpovídá běžnému času.
		/// </summary>
		public void RegisterDone()
		{
			if (!this._TimeRegisterActive)
				this._WorkTimeListSort();
			else
				this._WorkTimeRegisterTimeDone();
		}
		/// <summary>
		/// Přepočítá časy fází a čas celého průchodu z jednotlivých položek v seznamu WorkTimeLst
		/// </summary>
		private void _RecalcPassTime()
		{
			this._RecalcPassTime(this.AxisData.DatePlanTime);
		}
		/// <summary>
		/// Přepočítá časy fází a čas celého průchodu z jednotlivých položek v seznamu WorkTimeLst
		/// </summary>
		private void _RecalcPassTime(TimeRange axisTime)
		{
			// Připravím seznam fází a jejich sumární časy:
			Dictionary<OperationPhase, TimeRange> phaseTimeDict = new Dictionary<OperationPhase, TimeRange>();
			phaseTimeDict.Add(OperationPhase.MoveTime, TimeRange.Empty);
			phaseTimeDict.Add(OperationPhase.TBC, TimeRange.Empty);
			phaseTimeDict.Add(OperationPhase.TAC, TimeRange.Empty);
			phaseTimeDict.Add(OperationPhase.TEC, TimeRange.Empty);

			// Sumarizace časů fází z jednotlivých položek WorkTime:
			bool existsTime = false;
			foreach (WorkTimeCls workTime in this._WorkTimeLst)
			{
				if (phaseTimeDict.ContainsKey(workTime.Phase))
				{
					phaseTimeDict[workTime.Phase] = phaseTimeDict[workTime.Phase] + workTime.WorkTime;
					existsTime = true;
				}
			}

			// Pokud neexistuje žádný WorkTime, a je předán čas osy S, pak tento čas osy S vložím do fáze MoveTime,
			//  a následně se okolo něj dopočítají i časy okolních fází:
			if (!existsTime)
				phaseTimeDict[OperationPhase.MoveTime] = axisTime;

			// Pro fáze, které nemají zaregistrovaný žádný WorkTime, najdu nejbližší reálný čas na sousední fázi, která čas má:
			this._RecalcPassTimeOne(phaseTimeDict, TimeRange.TimeDirection.ToFuture);
			this._RecalcPassTimeOne(phaseTimeDict, TimeRange.TimeDirection.ToHistory);
			this._RecalcPassTimeInner(phaseTimeDict);

			// Výsledky vepíšu do diskrétních proměnných:
			this.TMcBeg = phaseTimeDict[OperationPhase.MoveTime].Begin;
			this.TMcEnd = phaseTimeDict[OperationPhase.MoveTime].End;
			this.TBcBeg = phaseTimeDict[OperationPhase.TBC].Begin;
			this.TBcEnd = phaseTimeDict[OperationPhase.TBC].End;
			this.TAcBeg = phaseTimeDict[OperationPhase.TAC].Begin;
			this.TAcEnd = phaseTimeDict[OperationPhase.TAC].End;
			this.TEcBeg = phaseTimeDict[OperationPhase.TEC].Begin;
			this.TEcEnd = phaseTimeDict[OperationPhase.TEC].End;
			this.WorkBegin = this.TMcBeg;
			this.WorkEnd = this.TEcEnd;

			// Stav registrace (this.RegisterState) neměníme, naším úkolem je jen přepočítat časy.
		}
		/// <summary>
		/// Pro vnější fáze (v soupisu phaseTimeDict), které nemají zaregistrovaný žádný WorkTime, 
		/// najdu nejbližší reálný čas na sousední vnitřnější fázi, která čas má (pořadí fází odpovídá danému směru času).
		/// </summary>
		/// <param name="phaseTimeDict"></param>
		/// <param name="operationPhases"></param>
		private void _RecalcPassTimeOne(Dictionary<OperationPhase, TimeRange> phaseTimeDict, TimeRange.TimeDirection timeDir)
		{
			// Pořadí fází:
			OperationPhase[] operationPhases = null;
			switch (timeDir)
			{
				case TimeRange.TimeDirection.ToFuture:
					operationPhases = new OperationPhase[] { OperationPhase.MoveTime, OperationPhase.TBC, OperationPhase.TAC, OperationPhase.TEC };
					break;
				case TimeRange.TimeDirection.ToHistory:
					operationPhases = new OperationPhase[] { OperationPhase.TEC, OperationPhase.TAC, OperationPhase.TBC, OperationPhase.MoveTime };
					break;
			}
			if (operationPhases == null) return;

			// Nalezení nejvnitřnější fáze bez času, nalezení nejbližšího času:
			DateTime edge = DateTime.MinValue;
			int phaseCnt = operationPhases.Length;
			int phaseEmp = -1;
			for (int p = 0; p < phaseCnt; p++)
			{	// Jdeme přes jednotlivé fáze v daném pořadí:
				OperationPhase phase = operationPhases[p];
				if (phaseTimeDict[phase].IsEmpty)
					// Tato fáze nemá WorkTime, je prázdná = budeme ji muset doplňovat:
					phaseEmp = p;
				else
				{
					// Tato fáze má WorkTime, převezmeme její krajní (bližší = reverzní) hranu a ukončíme hledání vnější hrany:
					edge = phaseTimeDict[phase].Edge(timeDir, true);
					break;
				}
			}

			// Vepsání nalezeného času do fází bez vlastního času:
			if (phaseEmp >= 0 && edge > DateTime.MinValue)
			{
				for (int p = phaseEmp; p >= 0; p--)
				{	// Projdeme vnější fáze, které jsou "bez času":
					OperationPhase phase = operationPhases[p];
					phaseTimeDict[phase] = new TimeRange(edge, edge);
				}
			}
		}
		/// <summary>
		/// Pro vnitřní fáze (v soupisu phaseTimeDict), které nemají zaregistrovaný žádný WorkTime, 
		/// vytvořím a uložím časový interval s nulovou délkou v čas odpovídajícím předešlé fází (ve směru ToFuture).
		/// </summary>
		/// <param name="phaseTimeDict"></param>
		private void _RecalcPassTimeInner(Dictionary<OperationPhase, TimeRange> phaseTimeDict)
		{
			// Zatím fixně směr ToFuture:
			OperationPhase[] operationPhases = new OperationPhase[] { OperationPhase.MoveTime, OperationPhase.TBC, OperationPhase.TAC, OperationPhase.TEC };

			int phaseCnt = operationPhases.Length;
			DateTime edge = DateTime.MinValue;
			for (int p = 0; p < phaseCnt; p++)
			{	// Jdeme přes jednotlivé fáze v daném pořadí (ToFuture):
				OperationPhase phase = operationPhases[p];
				if (phaseTimeDict[phase].IsEmpty)
				{	// Tato fáze nemá WorkTime, je prázdná = zkusíme ji doplnit:
					if (edge > DateTime.MinValue)
						phaseTimeDict[phase] = new TimeRange(edge, edge);
				}
				else
				{
					// Tato fáze má WorkTime, převezmeme její End (jdeme ToFuture) pro případné následující vnitřní fáze bez času:
					edge = phaseTimeDict[phase].End;
				}
			}
		}
		/// <summary>
		/// Přepočítá chybějící časy fází (TxcMis)
		/// </summary>
		private void _RecalcMissingTime()
		{
			// Připravím seznam fází a jejich zaregistrovaných kapacit:
			Dictionary<OperationPhase, decimal> phaseTimeDict = new Dictionary<OperationPhase, decimal>();
			phaseTimeDict.Add(OperationPhase.MoveTime, 0M);
			phaseTimeDict.Add(OperationPhase.TBC, 0M);
			phaseTimeDict.Add(OperationPhase.TAC, 0M);
			phaseTimeDict.Add(OperationPhase.TEC, 0M);

			// Sumarizace zaregistrovaných kapacit z jednotlivých položek WorkTime:
			foreach (WorkTimeCls workTime in this._WorkTimeLst)
			{
				if (phaseTimeDict.ContainsKey(workTime.Phase))
					phaseTimeDict[workTime.Phase] = phaseTimeDict[workTime.Phase] + workTime.PassCapacity;
			}

			// Výsledky vepíšu do diskrétních proměnných:
			this._TMcMis = this._RecalcMissingTimeOne(this._TMcReq, phaseTimeDict[OperationPhase.MoveTime]);
			this._TBcMis = this._RecalcMissingTimeOne(this._TBcReq, phaseTimeDict[OperationPhase.TBC]);
			this._TAcMis = this._RecalcMissingTimeOne(this._TAcReq, phaseTimeDict[OperationPhase.TAC]);
			this._TEcMis = this._RecalcMissingTimeOne(this._TEcReq, phaseTimeDict[OperationPhase.TEC]);

			// Stav registrace (this.RegisterState) neměníme, naším úkolem je jen přepočítat časy.
		}
		/// <summary>
		/// Vrátí chybějící kapacitu
		/// </summary>
		/// <param name="capacityRequired">Kapacita požadovaná</param>
		/// <param name="capacityRegistered">Kapacita dosud zaregistrovaná</param>
		/// <returns>Chybějící kapacita.</returns>
		private decimal _RecalcMissingTimeOne(decimal capacityRequired, decimal capacityRegistered)
		{
			decimal capacityMissing = capacityRequired - capacityRegistered;
			return (capacityMissing > 0M ? capacityMissing : 0M);
		}
		/// <summary>
		/// Vždy setřídí seznam časů
		/// </summary>
		private void _WorkTimeListSortIfNeed()
		{
			if (this._WorkTimeListUnsorted)
				this._WorkTimeListSort();
		}
		/// <summary>
		/// Vždy setřídí seznam časů
		/// </summary>
		private void _WorkTimeListSort()
		{
			this._WorkTimeLst.Sort(WorkTimeCls.CompareByBeginPhase);
			this._WorkTimeListUnsorted = false;
		}
		#endregion
		#region OBECNÉ METODY (SetTiming, SetPassQty, ClearResultData)
		/// <summary>
		/// Do tohoto tasku vloží informace o časování operace (směr, výchozí fáze, výchozí datum).
		/// Nutně musí dojít k vynulování (out) časů: TMcBeg, TMcEnd, ..., WorkBegin, WorkEnd a příznaků, viz metoda ClearResultData().
		/// </summary>
		/// <param name="timeDir">Směr alokace času</param>
		/// <param name="startPhase">Výchozí výrobní fáze, může být None = od první fáze v daném směru</param>
		/// <param name="startTime">Výchozí čas alokace tohoto kapacitního úkolu</param>
        /// <param name="resetFixedTime">Resetovat FixedTime (ignorovat jej), používá se při ručním přeplánování operace</param>
        internal void SetTiming(TimeRange.TimeDirection timeDir, OperationPhase startPhase, DateTime startTime, bool resetFixedTime)
		{
			this._TimeDir = timeDir;
			this._StartPhase = startPhase;
            if (!resetFixedTime && this._IsFromFixedPass && !this._FixedPassPrevTime.IsEmpty)
            {
                if (timeDir == TimeRange.TimeDirection.Stay)
                    this._StartTime = this._FixedPassPrevTime.End;
                else
                    this._StartTime = this._FixedPassPrevTime.Edge(timeDir, true);
            }
            else
    			this._StartTime = startTime;
			this.ClearResultData();
		}
		/// <summary>
		/// Nastaví do sebe dané hodnoty množství pro aktuální paralelní průběh.
		/// Metoda vyhodí chybu, pokud tento paralelní průběh již má předělené výrobní časy (nejprve je nutno kapacity odregistrovat).
		/// </summary>
		/// <param name="qtyFinal">Celkové množství vyrobené tímto průběhem (včetně již vyrobených kusů), ovlivní určení kapacit pro nevýrobní fáze</param>
		/// <param name="qtyRequired">Množství požadované do výroby v tomto průběhu, na toto množství se bude počítat TAC</param>
		/// <param name="unitCount">Množství vyrobené jedním cyklem TAC v tomto průchodu (může se lišit od UnitCount na operaci)</param>
		internal void SetPassQty(decimal qtyFinal, decimal qtyRequired, decimal unitCount)
		{
			if (this.ItemID != 0 && this._WorkTimeLst.Count > 0)
				Throw.SysError(MessageInfo.Get("Chybné řízení: nelze změnit množství jednoho paralelního průchodu v situaci, kdy má přidělené výrobní kapacity."));

			this.ClearResultData();
			this._QtyFinal = qtyFinal;
			this._QtyRequired = qtyRequired;
			this._UnitCountRequired = unitCount;
			this._UnitCount = unitCount;

			// Určit časy fází, potřebné pro registraci kapacit tohoto průchodu:
			CapacityRegisterCls.CalculateTaskPhaseTimeByOper((IOperationTime)this.Parent, (ITaskTimeData)this, qtyFinal, qtyRequired, unitCount);
		}
        /// <summary>
        /// Do své interní paměti this._SourceList uloží klon obdobného seznamu z Parenta.
        /// Jde o rozpis kapacitních zdrojů + jednotek.
        /// Výchozí stav pro každý kapacitní úkol je shodný se základem tasku, ale každý jednotlivý paralelní průchod může mít specifické nastavení (interaktivní přesuny).
        /// </summary>
        internal void CloneSourceFromParent()
        {
            this._SourceList = new List<ITaskSourceData>();
            foreach (ITaskSourceData parentSource in this.Parent.SourceList)
                this._SourceList.Add(new TaskSourceData(parentSource));
        }
		/// <summary>
		/// Vynuluje výstupní data = data, která do tasku vepisuje proces registrace kapacit.
		/// Jde o časy a výstupní příznaky.
		/// Task je tímto uveden do stavu, ve kterém byl založen na základě předané operace, před registrací kapacit.
		/// </summary>
		internal void ClearResultData()
		{
			this._WorkTimeCurrent = null;
			if (this.ItemID != 0)
				this._WorkTimeLst = new List<WorkTimeCls>();

			this._TMcBeg = DateTime.MinValue;
			this._TMcEnd = DateTime.MinValue;
			this._TBcBeg = DateTime.MinValue;
			this._TBcEnd = DateTime.MinValue;
			this._TAcBeg = DateTime.MinValue;
			this._TAcEnd = DateTime.MinValue;
			this._TEcBeg = DateTime.MinValue;
			this._TEcEnd = DateTime.MinValue;
			this._WorkBegin = DateTime.MinValue;
			this._WorkEnd = DateTime.MinValue;
			this._RegisterState = CapacityRegisterState.Initialized;
		}
		/// <summary>
		/// Metoda setřídí seznam položek časů WorkTimeLst.
		/// </summary>
		internal void WorkTimeListSort()
		{
			this._WorkTimeListSort();
		}
		#endregion
        #region PODPORA PRO EXPORT + IMPORT DAT PRO FIXOVANÉ TASKY, komunikuje s třídou PlanningParallelPassCls
        /// <summary>
        /// Vloží do sebe (WorkPass) čas fixované operace a příznak fixace = true, který následně bude pokládat za čas potřebný pro registraci kapacit
        /// </summary>
        /// <param name="timeRange"></param>
        internal void SetFixedTime(TimeRange timeRange)
        {
            this._IsFromFixedPass = true;
            this._FixedPassPrevTime = timeRange;
        }
        /// <summary>
        /// Vynuluje v sobě čas fixované operace a příznak fixace.
        /// </summary>
        /// <param name="timeRange"></param>
        internal void ClearFixedTime()
        {
            this._IsFromFixedPass = false;
            this._FixedPassPrevTime = TimeRange.Empty;
        }
        /// <summary>
        /// Metoda vrátí soupis kapacitních jednotek a pracovišť, o nichž ví, že jsou explicitně přidané / zakázané.
        /// Soupis se poté uloží ve třídě PlanningParallelPassCls, kde přečká dobu, kdy je plán smazán, 
        /// a odkud se znovu vloží do nově založených paralelních průchodů, viz metoda PlanningParallelPassCls.CreateWorkPassIntoTask() - volá this.SetFixedUnits().
        /// Seznam obsahuje: 
        /// a) opis interního pole this._UnitList, které je naplněno interaktivními přesuny v plánovací tabuli.
        /// b) pokud je pole this._UnitList prázdné (po načtení z databáze, kam se tato data neukládají) je seznam vytvořen z reálně použitých KPJ (z this.WorkTime, WorkUnit)
        /// </summary>
        /// <returns></returns>
        internal List<TaskChangeOneSourceUnitCls> GetFixedUnits()
        {
            List<TaskChangeOneSourceUnitCls> result = new List<TaskChangeOneSourceUnitCls>();
            if (this._UnitList != null && this._UnitList.Count > 0)
            {   // Existuje explicitní seznam => převezmeme jeho prvky:
                result.AddRange(this._UnitList);
            }
            else
            {   // Neexistuje => vytvoříme prvky typu Assigned z reálně se vyskytujících KPJ:
                foreach (WorkTimeCls time in this._WorkTimeLst)
                {
                    foreach (WorkUnitCls unit in time.WorkUnitLst)
                    {
                        TaskChangeOneSourceUnitCls item = new TaskChangeOneSourceUnitCls(unit.Category, 0, 0, unit.CapacitySource, unit.CapacityUnit, unit.UseRatio);
                        if (!result.Exists(i => i.IsEqualTo(item)))
                            result.Add(item);
                    }
                }
            }
            return result;
        }
        /// <summary>
        /// Metoda do sebe (WorkPass) vloží explicitně dodaný seznam kapacitních jednotek vyloučených a přikázaných, tak jak jej vytvořila metoda this.GetFixedUnits().
        /// </summary>
        /// <param name="list"></param>
        internal void SetFixedUnits(List<TaskChangeOneSourceUnitCls> list)
        {
            this._UnitList = new List<TaskChangeOneSourceUnitCls>();
            if (list != null)
                this._UnitList.AddRange(list);
        }
        /// <summary>
        /// Příznak, že tento paralelní průchod pochází z fixované operace, a má explicitně daný výchozí čas (_FixedPassPrevTime).
        /// Tento příznak spolu s hodnotou _FixedPassPrevTime řídí Timing průchodu (=tasku).
        /// V metodě this.SetTiming() se nepřevezme čas dodaný zvenku (z parametru), ale z _FixedPassPrevTime, podle směru času, prioritně ToHistory.
        /// </summary>
        private bool _IsFromFixedPass;
        /// <summary>
        /// Časový interval fixovaného paralelního průchodu.
        /// Sem se vkládá při tvorbě paralelního průchodu z dříve již zaplánovaného průchodu z fixované operace VP.
        /// Odtud se přebírá
        /// </summary>
        private TimeRange _FixedPassPrevTime;
        #endregion
        #region UMÍSTĚNÍ ELEMENTU (získání umístění z definice elementu, získání elementu podle definice umístění)
        /// <summary>
		/// Metoda vrátí informaci o relativním umístění určitého elementu v rámci jeho WorkPass.
		/// Tuto metodu běžně aplikace nepoužívá pro množství argumentů.
		/// Aplikace má číst hodnotu property WorkUnitCls.WorkUnitLocation, odkud se interně volá tato metoda s příslučnými parametry.
		/// </summary>
		/// <param name="operationKey">Klíč operace (Task.OperationKey). Podle tohoto klíče je možno operaci dohledat i v řetězu operací jednoho dílce.</param>
		/// <param name="category">Kategorie zdroje kapacit</param>
		/// <param name="capacitySource">Obecný zdroj kapacit</param>
		/// <param name="capacityUnit">Kapacitní jednotka</param>
		/// <param name="capacityDesk">Kapacitní stůl, index</param>
		/// <param name="timeId">ID položky času</param>
		/// <returns>Relativníumístění daného elementu</returns>
		internal WorkUnitLocationCls WorkUnitLocationGet(int operationKey, CapacitySourceCategory category, int capacitySource, int capacityUnit, int capacityDesk, int timeId)
		{
			// Najdeme fázi, která odpovídá danému času:
			OperationPhase phase = OperationPhase.None;
			foreach (WorkTimeCls time in this._WorkTimeLst)
			{	// Projdeme všechny položky daného času:
				if (time.TimeID == timeId)
				{
					phase = time.Phase;
					break;
				}
			}
			if (phase == OperationPhase.None) return null;

			// Projdeme znovu jednotlivé časy, sečteme sumární čas fáze, a určíme absolutní čas počátku a konce daného času timeId:
			decimal phaseTimeTotal = 0M;
			decimal elementTimeBegin = 0M;
			decimal elementTimeEnd = 0M;
			foreach (WorkTimeCls time in this._WorkTimeLst)
			{	// Projdeme všechny položky daného času, vybereme jen položky v odpovídající fázi:
				if (time.Phase != phase) continue;
				if (time.TimeID == timeId)
				{
					elementTimeBegin = phaseTimeTotal;
					elementTimeEnd = phaseTimeTotal + time.PassCapacity;
				}
				phaseTimeTotal += time.PassCapacity;
			}
			return new WorkUnitLocationCls(this.TaskID, this.PassID, operationKey, phase, category, capacitySource, capacityUnit, capacityDesk, phaseTimeTotal, elementTimeBegin, elementTimeEnd);
		}
		/// <summary>
		/// Pokusí se najít a vrátit element práce (WorkUnit) nejvhodnější k dané původní lokaci.
		/// Tato metoda neověřuje shodu klíče operace v lokátoru (OperationKey), za to zodpovídá ten, kdo tuto metodu volá.
		/// Pokud není možno nic najít, vrací null. K tomu může dojít jen tehdy, když:
		/// a) neexistuje žádná položka WorkTime pro danou fázi
		/// b) v žádné položce WorkTime (pro danou fázi) neexistuje ani jeden záznam WorkUnit.
		/// Jinak se snaží najít nejvhodnější (nejbližší) položku WorkTime a WorkUnit k danému lokátoru.
		/// </summary>
		/// <param name="location">Lokátor pozice. Popisuje fázi a relativní čas uvnitř fáze, popisuje typ kapacity (kategorii, zdroj, jednotku).</param>
		/// <returns></returns>
		internal WorkUnitCls FindUnitItem(WorkUnitLocationCls location)
		{
			// Seznam časů dané fáze, setříděný:
			List<WorkTimeCls> workTimeList = this._GetFilterSortedWorkTimeList(location.Phase, TimeRange.TimeDirection.ToFuture);
			if (workTimeList == null || workTimeList.Count == 0) return null;

			// 1. Určím nynější délku fáze podle lokátoru:
			//    Určím čas počátku a konce původního elementu v jednotkách aktuální fáze:
			decimal phaseTimeTotal = 0M;
			foreach (WorkTimeCls workTime in workTimeList)
				phaseTimeTotal += workTime.PassCapacity;
			decimal elementTimeBegin = phaseTimeTotal * location.PhaseTimeRatioBegin;
			decimal elementTimeEnd = phaseTimeTotal * location.PhaseTimeRatioEnd;

			// 2. Prohledám časy fáze, a zapamatuju si index času, který má největší překrytí s intervalem původního elementu:
			decimal phaseTimeBegin = 0M;
			int maxIndex = -1;
			decimal maxCommon = 0;
			for (int t = 0; t < workTimeList.Count; t++)
			{
				WorkTimeCls workTime = workTimeList[t];
				decimal phaseTimeEnd = phaseTimeBegin + workTime.PassCapacity;     // Kdy (v jaké sekundě fáze) končí tento WorkTime
				decimal commonTime = Library.GetIntersection(phaseTimeBegin, phaseTimeEnd, elementTimeBegin, elementTimeEnd);
				if (maxIndex < 0 || commonTime > maxCommon)
				{
					maxIndex = t;
					maxCommon = commonTime;
				}
				phaseTimeBegin = phaseTimeEnd;
			}
			if (maxIndex < 0) return null;

			// Našli jsme WorkTime odpovídající zadanému času => nechť on sám najde nejvhodnější element WorkUnit:
			WorkTimeCls maxWorkTime = workTimeList[maxIndex];
			return maxWorkTime.FindUnitItem(location);
		}
		#endregion
		#region ČTENÍ DAT O JEDNOTKÁCH PRÁCE A O VZTAZÍCH MEZI NIMI PRO JEJICH ZOBRAZOVÁNÍ
		/// <summary>
		/// Obsahuje true, pokud tento pracovní průchod obsahuje alespoň jednu položku času, obsahující nějaký WorkUnit.
		/// Jinými slovy, zda metoda this.CreateDataRelation() vygeneruje nějaká data.
		/// </summary>
		internal bool ContainAnyWork
		{
			get
			{
				foreach (WorkTimeCls workTime in this._WorkTimeLst)
				{
					if (workTime.ContainAnyWork) return true;
				}
				return false;
			}
		}
		/// <summary>
		/// Vrátí true, pokud někde v tomto WorkPass existuje WorkUnit s daným klíčem.
		/// </summary>
		/// <param name="workGID"></param>
		/// <returns></returns>
		internal bool ContainWorkUnit(GID workGID)
		{
			if (workGID.ClassNumber != Constants.ClassNumberWork) return false;
			foreach (WorkTimeCls workTime in this._WorkTimeLst)
			{
				if (workTime.ContainWorkUnit(workGID)) return true;
			}
			return false;
		}
		/// <summary>
		/// Do předaného soupisu workUnitList přidá všechny své záznamy WorkUnit.
		/// </summary>
		/// <param name="workUnitList"></param>
		internal void CreateWorkUnitList(ref List<WorkUnitCls> workUnitList)
		{
			foreach (WorkTimeCls workTime in this._WorkTimeLst)
				workTime.CreateWorkUnitList(ref workUnitList);
		}
		/// <summary>
		/// Naplní do daného seznamu WorkUnit ze sebe (ze svých časů), dané kategorie.
		/// </summary>
		/// <param name="category"></param>
		/// <param name="workUnitList"></param>
		internal void GetWorkUnitListOfCategory(CapacitySourceCategory category, ref List<WorkUnitCls> workUnitList)
		{
			foreach (WorkTimeCls workTime in this._WorkTimeLst)
				workTime.GetWorkUnitListOfCategory(category, ref workUnitList);
		}
		/// <summary>
        /// Souhrn klíčů (ID): this + vnořených Time + Work
        /// </summary>
        internal string DebugKeys
        {
            get
            {
                string text = "Pass " + this.PassID.ToString();
                if (this._WorkTimeLst.Count > 0)
                {
                    string prefix = "; TimeList = {";
                    foreach (WorkTimeCls workTime in this._WorkTimeLst)
                    {
                        text += prefix + workTime.DebugKeys;
                        prefix = ", ";
                    }
                    text += "}";
                }
                return text;
            }
        }
        /// <summary>
		/// Do předaného soupisu dataRelationList přidá záznamy o položkách a vztazích mezi položkami za tento pracovní průchod,
		/// tedy za všechny jeho časy a jejich pracovní jednotky.
		/// </summary>
		/// <param name="dataRelationList">Ref výstup pro soupis položek Work a Relation.</param>
		internal void CreateDataRelation(ref List<DataRelation> dataRelationList)
		{
			// Jdeme ve směru času Dopředu, nemá význam generovat vztahy zpětně:
			ElementRelationOrientation relation = ElementRelationOrientation.NextWork;
			WorkTimeCls lastTime = null;                  // Pro interní provázání jednotlivých WorkTime a WorkUnit
			for (int t = 0; t < this._WorkTimeLst.Count; t++)
			{
				WorkTimeCls workTime = this._WorkTimeLst[t];

				// 1. Pokud tento Time neobsahuje ani jednu položku WorkUnit, pak tento Time přeskočíme:
				if (!workTime.ContainAnyWork) continue;

				// 2. Výstup z jednoho Time: výstup všech WorkTime jako položek, a výstup jejich vzájemných (paralelních) vztahů:
				workTime.CreateDataRelation(ref dataRelationList);

				// 3. Vzájemné vztahy mezi dvěma Time řeší statická metoda třídy WorkTimeCls:
				if (lastTime != null)
					WorkTimeCls.CreateDataRelation(lastTime, workTime, relation, ref dataRelationList);

				// 4. Proměnná lastTime je registrátorem posledního času, který je neprázdný:
				lastTime = workTime;
			}
		}
		/// <summary>
		/// Metoda najde a do dodaného seznamu naplní použité kapacitní jednotky za všechny kategorie, včetně jejich použité kapacity.
		/// Provede ze všech časů.
		/// </summary>
		/// <param name="usedDict">Průběžně doplňovaný seznam KPJ a jejich časů</param>
		internal void CreatePlanUnitList(ref Dictionary<int, decimal> usedDict)
		{
			this.CreatePlanUnitList(CapacitySourceCategory.AllCategory, ref usedDict);
		}
		/// <summary>
		/// Metoda najde a do dodaného seznamu naplní použité kapacitní jednotky na dané kategorii, včetně jejich použité kapacity.
		/// Provede ze všech časů.
		/// </summary>
		/// <param name="sourceCategory">Požadovaný typ zdroje (např. pracoviště)</param>
		/// <param name="usedDict">Průběžně doplňovaný seznam KPJ a jejich časů</param>
		internal void CreatePlanUnitList(CapacitySourceCategory sourceCategory, ref Dictionary<int, decimal> usedDict)
		{
			foreach (WorkTimeCls workTime in this._WorkTimeLst)
				workTime.CreatePlanUnitList(sourceCategory, ref usedDict);
		}
		/// <summary>
		/// Metoda vrátí seznam datapointerů na všechny WorkUnity (ze všech Pass - Time - Unit).
		/// V dataPointeru je řádek = kapacitní jednotka, a element = WorkUnit.
		/// </summary>
		/// <returns></returns>
		internal void CreateAllWorkUnitPointers(ref List<DataPointerStr> pointerList)
		{
			foreach (WorkTimeCls workTime in this._WorkTimeLst)
				workTime.CreateAllWorkUnitPointers(ref pointerList);
		}
		/// <summary>
		/// Metoda zajistí naplnění seznamu fází jejich časy
		/// </summary>
		/// <param name="phaseDict"></param>
		internal void CreatePhaseTime(ref Dictionary<OperationPhase, TimeRange> phaseDict)
		{
			_CreatePhaseTimeAdd(OperationPhase.MoveTime, this.TimeTMC, ref phaseDict);
			_CreatePhaseTimeAdd(OperationPhase.TBC, this.TimeTBC, ref phaseDict);
			_CreatePhaseTimeAdd(OperationPhase.TAC, this.TimeTAC, ref phaseDict);
			_CreatePhaseTimeAdd(OperationPhase.TEC, this.TimeTEC, ref phaseDict);
		}
		/// <summary>
		/// Metoda přidá daný časový interval k určité fázi do souhrnné dictionary.
		/// </summary>
		/// <param name="phase">Typ výrobní fáze</param>
		/// <param name="time">Časový interval</param>
		/// <param name="phaseDict">Průběžně doplňovaný slovník fází a sumárních časů</param>
		private void _CreatePhaseTimeAdd(OperationPhase phase, TimeRange time, ref Dictionary<OperationPhase, TimeRange> phaseDict)
		{
			if (!phaseDict.ContainsKey(phase))
				phaseDict.Add(phase, TimeRange.Empty);
			phaseDict[phase] += time;
		}
        /// <summary>
        /// Tato metoda provede rekonstrukci časů jednotlivých výrobních dávek na základě svých vnitřních výrobních časů WorkTime.
        /// </summary>
        /// <param name="batchTask">Sada výrobních dávek (pouze list výrobních dávek, nikoli nadřízený controler)</param>
        internal void CapacityBatchRecalc(CapacityBatchTaskCls batchTask)
        {
            batchTask.OnePassPrepare(this);
            List<WorkTimeCls> workTimeList = this._GetSortedWorkTimeList(((ITaskRegData)this).TimeDir);
            foreach (WorkTimeCls workTime in workTimeList)
            {	// Jednotlivé časy, setříděné vzestupně, z nich vyberu jen TAC:
                if (workTime.Phase == OperationPhase.TAC)
                {	// A tyto časové úseky TAC přidám do registrátoru dávek:
                    CapacityBatchAddRegisteredTimeArgs args = new CapacityBatchAddRegisteredTimeArgs(this.TaskID, this.PassID, workTime.Phase, workTime.WorkTime, workTime.StandardRatio);
                    batchTask.TaskRegisterAddTime(args);
                }
            }
            batchTask.OnePassFinalize();
        }
		/// <summary>
		/// Tato metoda provede rekonstrukci časů jednotlivých výrobních dávek na základě svých vnitřních výrobních časů WorkTime.
		/// </summary>
		/// <param name="capacityBatchControl">Registrátor výrobních dávek (controler, vkládá data do svého registru Current)</param>
		internal void CapacityBatchRecalc(CapacityBatchControlCls capacityBatchControl)
		{   // Aktuálně se tato metoda nepoužívá.

            capacityBatchControl.OnePassPrepare(this);
            List<WorkTimeCls> workTimeList = this._GetSortedWorkTimeList(TimeRange.TimeDirection.ToFuture);
			foreach (WorkTimeCls workTime in workTimeList)
			{	// Jednotlivé časy, setříděné vzestupně, z nich vyberu jen TAC:
				if (workTime.Phase == OperationPhase.TAC)
				{	// A tyto časové úseky TAC přidám do registrátoru dávek:
					CapacityBatchAddRegisteredTimeArgs args = new CapacityBatchAddRegisteredTimeArgs(this.TaskID, this.PassID, workTime.Phase, workTime.WorkTime, workTime.StandardRatio);
					capacityBatchControl.TaskRegisterAddTime(args);
				}
			}
            capacityBatchControl.OnePassFinalize();
		}
		/// <summary>
		/// Metoda najde a vrátí svůj OuterPointer.
		/// OuterPointer je adresa objektu WorkUnit, který je v daném směru vnějším v rámci této jednotky.
		/// Tato jednotka (WorkPass) tedy najde svůj krajní čas v daném směru (viz parametry), 
		/// a v něm najde nejvhodnější pracovní jednotku WorkUnit (první z existujících, v pořadí Workplace, Profession, Resource) a vrátí její pointer.
		/// Tento OuterPointer slouží k provazování sousedních operací a sousedních dílců.
		/// WorkPass není zodpovědný za hledání tohoto bodu v jiných WorkPass.
		/// Pokud tento Pass nemá žádný WorkUnit, vrací Empty.
		/// </summary>
		/// <param name="timeDir">Směr běhu času: 
		/// ToFuture při reverseDir = false vrací pointer na časově poslední záznam. 
		/// ToHistory při reverseDir = false vrací pointer na časově první záznam.
		/// </param>
		/// <param name="reverseDir">Obraceč času:
		/// ToFuture při reverseDir = true vrací pointer na časově první záznam. 
		/// ToHistory při reverseDir = true vrací pointer na časově poslední záznam.
		/// </param>
		/// <param name="withAnyWork">Požadavek, aby vrácený WorkTime obsahoval alespoň jednu položku WorkUnit (true) 
		/// / nebo false = vzít zcela krajní WorkTime bez ohledu na jeho WorkUnit</param>
		/// <param name="outerTime">Out parametr, kam je vložen krajní čas (DateTime) z krajní položky WorkTime.</param>
		/// <returns>Pointer na nejvhodnější krajní element. Pokud tento Pass nemá žádný WorkUnit, vrací Empty.</returns>
		internal DataPointerStr GetOuterPointer(TimeRange.TimeDirection timeDir, bool reverseDir, bool withAnyWork, out DateTime outerTime)
		{
			DataPointerStr result = DataPointerStr.Empty;
			WorkTimeCls outerWorkTime = this.GetOuterWorkTime(timeDir, reverseDir, withAnyWork, out outerTime);
			if (outerWorkTime != null)
				result = outerWorkTime.GetOuterPointer();

			return result;
		}
		/// <summary>
		/// Najde a vrátí jeden ze svých časů, nejbližší danému okraji, který obsahuje WorkUnits.
		/// </summary>
		/// <param name="timeDir">Směr běhu času: 
		/// ToFuture při reverseDir = false vrací pointer na časově poslední záznam. 
		/// ToHistory při reverseDir = false vrací pointer na časově první záznam.
		/// </param>
		/// <param name="reverseDir">Obraceč času:
		/// ToFuture při reverseDir = true vrací pointer na časově první záznam. 
		/// ToHistory při reverseDir = true vrací pointer na časově poslední záznam.
		/// </param>
		/// <param name="withAnyWork">Požadavek, aby vrácený WorkTime obsahoval alespoň jednu položku WorkUnit (true) 
		/// / nebo false = vzít zcela krajní WorkTime bez ohledu na jeho WorkUnit</param>
		/// <returns></returns>
		internal WorkTimeCls GetOuterWorkTime(TimeRange.TimeDirection timeDir, bool reverseDir, bool withAnyWork)
		{
			DateTime outerTime;
			return this.GetOuterWorkTime(timeDir, reverseDir, withAnyWork, out outerTime);
		}
		/// <summary>
		/// Najde a vrátí jeden ze svých časů, nejbližší danému okraji, který obsahuje WorkUnits.
		/// </summary>
		/// <param name="timeDir">Směr běhu času: 
		/// ToFuture při reverseDir = false vrací pointer na časově poslední záznam. 
		/// ToHistory při reverseDir = false vrací pointer na časově první záznam.
		/// </param>
		/// <param name="reverseDir">Obraceč času:
		/// ToFuture při reverseDir = true vrací pointer na časově první záznam. 
		/// ToHistory při reverseDir = true vrací pointer na časově poslední záznam.
		/// </param>
		/// <param name="withAnyWork">Požadavek, aby vrácený WorkTime obsahoval alespoň jednu položku WorkUnit (true) 
		/// / nebo false = vzít zcela krajní WorkTime bez ohledu na jeho WorkUnit</param>
		/// <param name="outerTime">Out parametr, kam je vložen krajní čas (DateTime) z krajní položky WorkTime.</param>
		/// <returns></returns>
		internal WorkTimeCls GetOuterWorkTime(TimeRange.TimeDirection timeDir, bool reverseDir, bool withAnyWork, out DateTime outerTime)
		{
			outerTime = DateTime.MinValue;
			// Sestavím setříděný seznam všech položek WorkTime, v opačném než daném směru (ToFuture chce jít od konce !!!)
			List<WorkTimeCls> workTimeList = this._GetSortedWorkTimeList(timeDir, !reverseDir);
			foreach (WorkTimeCls workTime in workTimeList)
			{
				if (!withAnyWork || (withAnyWork && workTime.ContainAnyWork))
				{	// Pokud není požadavek (withAnyWork) na obsah WorkUnit, anebo (pokud je požadavek, a tento WorkTime obsahuje WorkUnit):
					outerTime = workTime.WorkTime.Edge(timeDir, reverseDir);
					return workTime;
				}
			}
			return null;
		}
		/// <summary>
		/// Metoda najde a vrátí položku času WorkTime, která jako první vyhovuje danému vektoru času.
		/// Pokud nevyhovuje žádná, vrací null.
		/// </summary>
		/// <param name="timeVector">Výchozí okamžik a směr hledání</param>
		/// <param name="withAnyWork">Požadavek, aby vrácený čas obsahoval alespoň jednu položku WorkUnit</param>
		/// <param name="findOuterWhenNull">Požadavek, aby se v případě nenalezení vyhovujícího času našel a vrátil čas zcela krajní</param>
		/// <returns></returns>
		internal WorkTimeCls FindWorkTime(TimeVector timeVector, bool withAnyWork, bool findOuterWhenNull)
		{
			List<WorkTimeCls> workTimeList = this._GetSortedWorkTimeList(timeVector.Direction); // Setříděný seznam všech položek WorkTime, v daném směru
			foreach (WorkTimeCls workTime in workTimeList)
			{
				if (timeVector.IsPositiveTime(workTime.WorkTime))
				{	// Pokud z pohledu daného vektoru je čas WorkTime pozitivní,
					//   (pozn. Všechny časy mají nenulovou kapacitu. Žádná položka WorkTime nemá čas nulový, pak by tady ani nebyla).
					if (!withAnyWork || (withAnyWork && workTime.ContainAnyWork))
					{	// ...a pokud není požadavek (withAnyWork) na obsah WorkUnit, 
						//    anebo (pokud je požadavek, a tento WorkTime obsahuje WorkUnit):
						return workTime;
					}
				}
			}

			// Nenašel jsem vyhovující čas, mám zkusit hledat prostý krajní čas?
			if (findOuterWhenNull)
				return this.GetOuterWorkTime(timeVector.Direction, false, withAnyWork);
			
			return null;
		}
		/// <summary>
		/// Vyhledá v paměti konkrétní časový úsek WorkTimeCls podle jeho GIDu.
		/// Tato metoda nemůže použít žádný index, hledá položku pomocí postupného scanování všech tasků -» pass -» time.
		/// Měla by být používána v minimální míře z důvodu časové náročnosti (systém na toto hledání není optimalizován).
		/// Metoda může vrátit null, pokud nic nenajde.
		/// </summary>
		/// <param name="timeGId"></param>
		/// <returns></returns>
		internal WorkTimeCls SearchForWorkTime(GID timeGId)
		{
			if (timeGId.ClassNumber != Constants.ClassNumberTime && timeGId.ClassNumber != PlanUnitCTimeCls.ClassNr) return null;
			return (this._WorkTimeLst.FirstOrDefault(t => (t.TimeID == timeGId.RecordNumber)));
		}
		/// <summary>
		/// Metoda vygeneruje nový List s položkami WorkTime, setřídí jej podle požadavku a vrátí.
		/// </summary>
		/// <param name="timeDir">Směr třídění: ToFuture = od prvního do posledního, ToHistory = od posledního do prvního. Třídí se podle času Begin a podle fáze.</param>
		/// <returns></returns>
		private List<WorkTimeCls> _GetSortedWorkTimeList(TimeRange.TimeDirection timeDir)
		{
			return _GetSortedWorkTimeList(timeDir, false);
		}
		/// <summary>
		/// Metoda vygeneruje nový List s položkami WorkTime, setřídí jej podle požadavku a vrátí
		/// </summary>
		/// <param name="timeDir">Směr třídění: ToFuture = od prvního do posledního, ToHistory = od posledního do prvního. Třídí se podle času Begin a podle fáze.</param>
		/// <param name="reverseDir">Reverze času: při hodnotě true třídí opačně, než je dáno směrem času.</param>
		/// <returns></returns>
		private List<WorkTimeCls> _GetSortedWorkTimeList(TimeRange.TimeDirection timeDir, bool reverseDir)
		{
			List<WorkTimeCls> workTimeList = new List<WorkTimeCls>(this._WorkTimeLst);
			this._SortWorkTimeList(ref workTimeList, timeDir, reverseDir);
			return workTimeList;
		}
		/// <summary>
		/// Metoda vygeneruje nový List s položkami WorkTime pro požadovanou fázi, setřídí jej podle požadavku a vrátí
		/// </summary>
		/// <param name="phase">Požadovaná fáze výroby, jejíž položky WorkTime chceme získat</param>
		/// <param name="timeDir">Směr třídění: ToFuture = od prvního do posledního, ToHistory = od posledního do prvního. Třídí se podle času Begin a podle fáze.</param>
		/// <returns></returns>
		private List<WorkTimeCls> _GetFilterSortedWorkTimeList(OperationPhase phase, TimeRange.TimeDirection timeDir)
		{
			return _GetFilterSortedWorkTimeList(phase, timeDir, false);
		}
		/// <summary>
		/// Metoda vygeneruje nový List s položkami WorkTime pro požadovanou fázi, setřídí jej podle požadavku a vrátí
		/// </summary>
		/// <param name="phase">Požadovaná fáze výroby, jejíž položky WorkTime chceme získat</param>
		/// <param name="timeDir">Směr třídění: ToFuture = od prvního do posledního, ToHistory = od posledního do prvního. Třídí se podle času Begin a podle fáze.</param>
		/// <param name="reverseDir">Reverze času: při hodnotě true třídí opačně, než je dáno směrem času.</param>
		/// <returns></returns>
		private List<WorkTimeCls> _GetFilterSortedWorkTimeList(OperationPhase phase, TimeRange.TimeDirection timeDir, bool reverseDir)
		{
			List<WorkTimeCls> workTimeList = this._WorkTimeLst.FindAll(delegate(WorkTimeCls t) { return (t.Phase == phase); });
			this._SortWorkTimeList(ref workTimeList, timeDir, reverseDir);
			return workTimeList;
		}
		/// <summary>
		/// Setřídí daný seznam WorkTime podle požadavku
		/// </summary>
		/// <param name="workTimeList">Seznam položek WorkTime, tento bude setříděn (přímo tento objekt, nebude se vytvářet kopie pro třídění!)</param>
		/// <param name="timeDir">Směr třídění: ToFuture = od prvního do posledního, ToHistory = od posledního do prvního. Třídí se podle času Begin a podle fáze.</param>
		/// <param name="reverseDir">Reverze času: při hodnotě true třídí opačně, než je dáno směrem času.</param>
		private void _SortWorkTimeList(ref List<WorkTimeCls> workTimeList, TimeRange.TimeDirection timeDir, bool reverseDir)
		{
			TimeRange.TimeDirection dir = (!reverseDir ? timeDir : TimeRange.ReverseDirection(timeDir));
			switch (dir)
			{
				case TimeRange.TimeDirection.ToFuture:
					workTimeList.Sort(WorkTimeCls.CompareByBeginPhase);
					break;
				case TimeRange.TimeDirection.ToHistory:
					workTimeList.Sort(WorkTimeCls.CompareByBeginPhaseDesc);
					break;
			}
		}
		#endregion
		#region PODPORA PRO INTERAKTIVNÍ ZMĚNU REGISTRACE KAPACITY PO POLOŽKÁCH TIME
		/// <summary>
		/// Příznak režimu registrace "Po položkách WorkTime"
		/// </summary>
		private bool _TimeRegisterActive = false;
		/// <summary>
		/// Seznam položek WorkTime, insertovací = do něj se vkládají položky času při registraci po položkách WorkTime.
		/// V tomto odděleném seznamu se pak setřídí, a z něj se vloží na místo původní položky času 
		/// na index [_TimeRegisterFirstIndex] do pole _WorkTimeLst.
		/// </summary>
		private List<WorkTimeCls> _WorkTimeInsertLst;
		/// <summary>
		/// Index položky WorkTime, která se bude odebírat (původní položka) a na její místo se budou vkládat nově zaregistrované položky.
		/// </summary>
		internal int TimeRegisterTargetIndex { get { return this._TimeRegisterTargetIndex; } }
		/// <summary>
		/// Index položky WorkTime, která je první vložená (s nejnižším časem Begin) na místo původního času.
		/// Pokud není insertována žádná položka, je rovno -1.
		/// </summary>
		internal int TimeRegisterInsertFromIndex { get { return this._TimeRegisterInsertFromIndex; } }
		/// <summary>
		/// Index položky WorkTime, která je poslední vložená (s nejvyšším časem Begin) na místo původního času.
		/// Pokud je insertována jen jedna položka, je hodnota rovna TimeRegisterInsertFromIndex.
		/// Pokud není insertována žádná položka, je rovno -1.
		/// </summary>
		internal int TimeRegisterInsertToIndex { get { return this._TimeRegisterInsertToIndex; } }
		/// <summary>
		/// Index položky WorkTime, která se bude odebírat (původní položka) a na její místo se budou vkládat nově zaregistrované položky.
		/// </summary>
		private int _TimeRegisterTargetIndex;
		/// <summary>
		/// Index položky WorkTime, která se zaregistrovala a vložila jako první = má nejnižší čas Begin.
		/// </summary>
		private int _TimeRegisterInsertFromIndex;
		/// <summary>
		/// Index položky WorkTime, která se zaregistrovala a vložila jako poslední = má nejvyšší čas Begin.
		/// </summary>
		private int _TimeRegisterInsertToIndex;
		/// <summary>
		/// Vrátí index podle daného směru:
		/// pro ToFuture vrátí TimeRegisterInsertToIndex,
		/// pro ToHistory vrátí TimeRegisterInsertFromIndex.
		/// </summary>
		/// <param name="timeDir">Požadovaný směr: pro ToFuture vrátí TimeRegisterInsertToIndex, pro ToHistory vrátí TimeRegisterInsertFromIndex.</param>
		/// <returns></returns>
		internal int TimeRegisterInsertGetIndex(TimeRange.TimeDirection timeDir)
		{
			return TimeRegisterInsertGetIndex(timeDir, false);
		}
		/// <summary>
		/// Vrátí index podle daného směru:
		/// pro ToFuture vrátí TimeRegisterInsertToIndex,
		/// pro ToHistory vrátí TimeRegisterInsertFromIndex.
		/// </summary>
		/// <param name="timeDir">Požadovaný směr: pro ToFuture vrátí TimeRegisterInsertToIndex, pro ToHistory vrátí TimeRegisterInsertFromIndex.</param>
		/// <param name="reverse">Obraceč směru: pokud je true, pak pro ToFuture vrátí TimeRegisterInsertFromIndex, pro ToHistory vrátí TimeRegisterInsertToIndex.</param>
		/// <returns></returns>
		internal int TimeRegisterInsertGetIndex(TimeRange.TimeDirection timeDir, bool reverse)
		{
			switch (timeDir)
			{
				case TimeRange.TimeDirection.ToFuture:
					return (reverse ? TimeRegisterInsertFromIndex : TimeRegisterInsertToIndex);
				case TimeRange.TimeDirection.ToHistory:
					return (reverse ? TimeRegisterInsertToIndex : TimeRegisterInsertFromIndex);
			}
			return -1;
		}
		/// <summary>
		/// Připraví this objekt pro změnu registrace položek času.
		/// Tato metoda neřeší odregistraci, jen se připraví do stavu registrace "Po položkách WorkTime".
		/// Metoda se musí volat jen jedenkrát, před začátkem odregistrování kapacit. Opakované volání způsobí nekonzistenci indexu this.TimeRegisterTargetIndex.
		/// </summary>
		internal void TimeRegisterPrepare()
		{
			if (!this._TimeRegisterActive)
			{	// Při prvním volání metody TimeRegisterPrepare():
				// this._WorkTimeListSort();    Nesmí se, jinak chybuje při registraci v dalších krocích před úplným koncem.
				this._WorkTimeInsertLst = new List<WorkTimeCls>();
			}
			this._TimeRegisterTargetIndex = -1;
			this._TimeRegisterInsertFromIndex = -1;
			this._TimeRegisterInsertToIndex = -1;
			this._TimeRegisterActive = true;
		}
		/// <summary>
		/// Metoda najde index položky WorkTime, která se bude odregistrovávat.
		/// Střádá si do this.TimeRegisterTargetIndex nejnižší nalezený index, kam bude následně vkládat nově zaregistrované položky WorkTime.
		/// </summary>
		/// <param name="timeId">ID položky WorkTime, která se bude jako první odregistrovávat, na její místo se budou vkládat nově registrované položky WorkTime</param>
		internal void TimeRegisterFindUnregisteredIndex(int timeId)
		{
			int targetIndex = this._WorkTimeLst.FindIndex(delegate(WorkTimeCls wt) { return (wt.TimeID == timeId); });
			if (targetIndex >= 0 && (this._TimeRegisterTargetIndex < 0 || targetIndex < this._TimeRegisterTargetIndex))
				this._TimeRegisterTargetIndex = targetIndex;
		}
		/// <summary>
		/// Metoda je volaná po dokončení registrace jednotlivých položek Time ve stavu registrace "Po položkách WorkTime".
		/// Zde dojde k setřídění nových položek (v seznamu this._WorkTimeInsertLst) 
		/// a k jejich vložení do trvalého soupisu this._WorkTimeLst na index _TimeRegisterFirstIndex, 
		/// a k aktualizaci hodnoty indexu _TimeRegisterLastIndex.
		/// </summary>
		private void _WorkTimeRegisterTimeDone()
		{
			int insCnt = this._WorkTimeInsertLst.Count;
			if (insCnt <= 0) return;

			// Setřídit nově zaregistrované položky WorkTime:
			if (this._WorkTimeInsertLst.Count > 1)
				this._WorkTimeInsertLst.Sort(WorkTimeCls.CompareByBeginPhase);

			// Najít index, kam budu vkládat nové položky:
			int insertIndex = this._TimeRegisterTargetIndex;

			// Vložit položky z _WorkTimeInsertLst (pracovní, jen ty nové) do trvalého seznamu _WorkTimeLst na pozici původní položky:
			if (insertIndex < 0 || insertIndex >= this._WorkTimeLst.Count)
			{
				this._TimeRegisterInsertFromIndex = this._WorkTimeLst.Count;
				this._WorkTimeLst.AddRange(this._WorkTimeInsertLst);
			}
			else
			{
				this._TimeRegisterInsertFromIndex = insertIndex;
				this._WorkTimeLst.InsertRange(insertIndex, this._WorkTimeInsertLst);
			}

			// Zapamatuji si index posledního vloženého prvku:
			this._TimeRegisterInsertToIndex = this._TimeRegisterInsertFromIndex + insCnt - 1;

			// Seznam časů (this._WorkTimeLst) se nyní nesmí setřídit, jinak navazující kontroly nebudou dobře fungovat:
			this._WorkTimeListUnsorted = false;

			// Přepočítat časy fází a čas celého průchodu z jednotlivých položek WorkTime:
			this._RecalcPassTime();
		}
		/// <summary>
		/// Ukončí režim registrace "Po položkách WorkTime" pro tento objekt.
		/// Pouze zruší příznak stavu, a zruší privátní pomocný soupis pro registraci v tomto režimu.
		/// Netřídí hlavní seznam WorkTimeList.
		/// </summary>
		internal void TimeRegisterDone()
		{
			this._TimeRegisterActive = false;
			this._WorkTimeInsertLst = null;
		}
		/// <summary>
		/// Metoda ověří, zda počínaje daným indexem položky WorkTime jsou položky WorkTime nekonfliktní.
		/// Pokud najde konflikt, vygeneruje objekt třídy CapacityTimeRegData, který popisuje potřebný čas pro přeregistrování kapacity
		/// a vrátí jej. Pokud není třeba přeregistrovat kapacitu, vrátí null.
		/// Soupis položek WorkTime, které je třeba odregistrovat, je uveden v CapacityTimeRegData
		/// </summary>
		/// <param name="index"></param>
		/// <param name="timeDir"></param>
		/// <returns></returns>
		internal CapacityTimeRegData TimeRegisterResultCheck(int index, PlanningDataCls.InteractiveAdjacentArgs adjArgs)
		{
			TimeRange.TimeDirection timeDir = adjArgs.TimeDir;
			//LimitedCType limitedCType

			int indexFirst = index;
			int timeCnt = this._WorkTimeLst.Count;
			if (indexFirst < 0 || indexFirst >= timeCnt) return null;  // Zadaný WorkTime neexistuje

			int indexAdd = (int)timeDir;
			if (indexAdd == 0) return null;
			indexAdd = Math.Sign(indexAdd);                          // Směr postupu na další WorkTime: +1 pro ToFuture,  -1 pro ToHistory
			
			WorkTimeCls timeFirst = this._WorkTimeLst[indexFirst];   // Výchozí položka WorkTime = ta, která je poslední nově zaregistrovaná, protí ní kontroluji návaznosti
			WorkTimeCls timeBegin = timeFirst;                       // První položka, která by se mohla přeregistrovat (to se ale ještě uvidí)
			DateTime edgeTime = timeFirst.WorkTime.Edge(timeDir);    // Pro ToFuture vrací End, pro ToHistory vrací Begin.

			DateTime startTime = timeFirst.WorkTime.Edge(timeDir, true);    // Začátek registrace, pokud budeme přeregistrovávat i výchozí WorkTime (pro ToFuture vrací Begin, pro ToHistory vrací End).
			decimal capacity = 0M;
			OperationPhase phase = timeFirst.Phase;                  // Fáze, pro kterou budeme registrovat kapacitu
			List<WorkTimeCls> unregisterTimeList = new List<WorkTimeCls>(); // Tyto prvky budeme odregistrovávat

			int indexNext = indexFirst;
			while (true)
			{
				indexNext += indexAdd;                               // Index další položky
				if (indexNext < 0 || indexNext >= timeCnt) break;    // Další položka WorkTime už není.
				WorkTimeCls timeTest = this._WorkTimeLst[indexNext];

				// Určím základní vztah mezi datumem hrany a fází, a testovanou položkou:
				_TimeAffinityType affinity = this._TimeRegisterDetectTimeAffinity(edgeTime, phase, timeTest, adjArgs);
				if (affinity == _TimeAffinityType.Join)
				{	// Join = Položky timeBegin a timeTest na sebe těsně navazují a mají shodnou fázi, zkusíme je spojit do jedné:
					if (unregisterTimeList.Count == 0)
					{
						unregisterTimeList.Add(timeBegin);
						capacity += timeBegin.PassCapacity;
					}
					unregisterTimeList.Add(timeTest);
					capacity += timeTest.PassCapacity;

					continue;
				}
				if (affinity == _TimeAffinityType.Conflict || affinity == _TimeAffinityType.Pull)
				{	// Položky timeBegin a timeTest se překrývají, musíme vyřešit konflikt:
					if (timeTest.Phase != phase)
					{	// Pokud se liší fáze prvku timeTest proti fázi výchozí:
						//  a pokud už máme něco jiného připraveno pro přeregistraci, pak tento prvek timeTest přidávat nelze (má jinou fázi):
						if (unregisterTimeList.Count > 0) break;

						// Tedy: liší se fáze, ale dosud není nic připraveno k přeregistrování kapacity: 
						//  => přeregistrace začne pro tuto novou fázi, a to prvkem timeTest:
						timeBegin = timeTest;                 // Výchozí prvek pro přeregistraci bude tento (timeTest), ne první (timeFirst)
						phase = timeTest.Phase;               // Registrovaná fáze bude fáze prvku timeTest, 
						startTime = edgeTime;                 // Počáteční čas registrace bude čas konce prvku timeFirst
						// Připravíme prvek timeTest k odregistrování:
						unregisterTimeList.Add(timeTest);
						capacity += timeTest.PassCapacity;
					}
					else
					{	// Prvek timeTest má stejnou fázi, jako dosud řešíme, a je v konfliktu s datem konce položky First:
						if (unregisterTimeList.Count == 0)
						{
							unregisterTimeList.Add(timeBegin);
							capacity += timeBegin.PassCapacity;
						}
						unregisterTimeList.Add(timeTest);
						capacity += timeTest.PassCapacity;
					}

					continue;
				}

				// Není ani Join, ani konflikt - skončíme:
				break;
			}

			/*    SLOVNÍ POPIS:
			 
			a) pokud první následující WorkTime je nenavazující, pak nic dalšího neřeším 
			b) pokud první následující WorkTime je těsně navazující, pak řeším zda jej lze spojit (a i další těsně navazující)
			     což lze při jen shodě fáze: počínaje Begin, přidám každou další položku WorkTime
			c) pokud následující WorkTime je konfliktní (začíná dřív, než First končí (ToFuture) nebo končí později než First začíná (ToHistory))
			     pak záleží na fázi:
			  c1) fáze je shodná jako fáze prvku First: tento WorkTime budeme slučovat s WorkTime First
			  c2) fáze je rozdílná:
			     c2a) už máme něco připraveného na slučování (prvek WorkTime First + další WorkTime shodné fáze):
			           pak tento prvek už slučovat nebudeme a vrátíme dosavadní data pro sloučení 
			     c2b) zatím není na slučování nic připraveného (to je v situaci, kdy prvek WorkTime First má jinou fázi než testovaný WorkTime)
			           pak přejdeme do režimu, kdy budeme přeregistrovávat prvky WorkTime počínaje tímto testovaným, 
			           v jeho fázi, počínaje časem First.End. Pak jdeme dál, a slučujeme prvky nové fáze, pokud jsou konfliktní k času First.End
			PS: obrázek by byl lepší.
			 
			 */

			// Pokud není nic ke změně, vrátím null:
			if (unregisterTimeList.Count == 0) return null;

			// Na závěr sestavím objekt třídy CapacityTimeRegData, který popisuje čas, který je třeba znovu zaregistrovat:
			CapacityTimeRegData timeData = new CapacityTimeRegData(phase, capacity, adjArgs.LimitedCType, timeDir, startTime, timeDir, startTime);
			timeData.UnregisterWorkTimeList = unregisterTimeList;
			return timeData;
		}
		/// <summary>
		/// Určí vzájemný vztah položek času 1 a 2
		/// </summary>
		/// <param name="edgeTime">Koncový čas první položky WorkTime = čas, ke kterému skončila registrace kapacit předešlého úseku</param>
		/// <param name="phase">Fáze, která se nyní přeregistrovává</param>
		/// <param name="timeTest">Položka času, která má zkontrolovat</param>
		/// <param name="timeDir">Směr času</param>
		/// <param name="timeConflict">Out: true = časy jsou v konfliktu, překrývají se  /  false = časy jsou návazné</param>
		/// <param name="timeToJoin">Out: true = časy lze sloučit do jednoho (přesně navazují, a jde o shodnou fázi) /  false = nelze to</param>
		private _TimeAffinityType _TimeRegisterDetectTimeAffinity(DateTime edgeTime, OperationPhase phase, WorkTimeCls timeTest, PlanningDataCls.InteractiveAdjacentArgs adjArgs)
		{
			switch (adjArgs.TimeDir)
			{
				case TimeRange.TimeDirection.ToFuture:
					if (timeTest.WorkTime.Begin < edgeTime) return _TimeAffinityType.Conflict;
					if ((timeTest.WorkTime.Begin == edgeTime) && (timeTest.Phase == phase)) return _TimeAffinityType.Join;
					if (adjArgs.PullWhenPossible) return _TimeAffinityType.Pull;
					return _TimeAffinityType.None;
				case TimeRange.TimeDirection.ToHistory:
					if (timeTest.WorkTime.End > edgeTime) return _TimeAffinityType.Conflict;
					if ((timeTest.WorkTime.End == edgeTime) && (timeTest.Phase == phase)) return _TimeAffinityType.Join;
					if (adjArgs.PullWhenPossible) return _TimeAffinityType.Pull;
					return _TimeAffinityType.None;
			}
			return _TimeAffinityType.None;
		}
		/// <summary>
		/// V jakém vztahu k sobě jsou dvě položky WorkTime při jejich posunování.
		/// Jakou změnu položky Test doporučuje metoda _TimeRegisterDetectTimeAffinity.
		/// </summary>
		private enum _TimeAffinityType
		{
			/// <summary>
			/// Žádná změna.
			/// Značí, že položky nejsou v časovém konfliktu, anebo jsou těsně navazující ale v jiné fázi. Nelze je lépe umístit.
			/// </summary>
			None,
			/// <summary>
			/// Spojit položky. Těsně navazují, a jsou ze stejné fáze. Odebrat obě a znovu zaregistrovat.
			/// </summary>
			Join,
			/// <summary>
			/// Konflikt položek. Odebrat testovací a správně zaregistrovat.
			/// </summary>
			Conflict,
			/// <summary>
			/// Přitáhnout položku. Obdobné jako Conflict, ale z jiného důvodu: mezi základnou a testovanou položkou je volný čas.
			/// </summary>
			Pull
		}
		#endregion
		#region OBSLUHA SEZNAMU EXPLICITNÍCH POŽADAVKŮ this.UnitList
		/// <summary>
		/// Z interního seznamu this.UnitList odebere všechny záznamy týkající se dané KPJ
		/// </summary>
		internal void UnitListRemove(int planUnitC)
		{
			for (int i = this._UnitList.Count - 1; i >= 0; i--)
			{
				if (this._UnitList[i].CapacityUnitExclude == planUnitC)
					this._UnitList.RemoveAt(i);
				if (this._UnitList[i].CapacityUnitAssign == planUnitC)
					this._UnitList.RemoveAt(i);
			}
		}
		/// <summary>
		/// Do interního seznamu explicitních požadavků na použití KPJ přidá další záznam (parametr).
		/// </summary>
		internal void UnitListAddItem(TaskChangeOneSourceUnitCls item)
		{
			this._UnitListAddOnce(item);
		}
		/// <summary>
		/// Vymaže obsah interního seznamu explicitních požadavků na použití KPJ. 
		/// Příští registrace kapacit proběhne pouze podle běžných pravidel, bez výjimek.
		/// </summary>
		internal void UnitListClear()
		{
			this._UnitList.Clear();
		}
		/// <summary>
		/// Do seznamu this._UnitList přidá novou položku (item), pokud tam dosud neexistuje položka se shodnými daty
		/// </summary>
		/// <param name="item"></param>
		private void _UnitListAddOnce(TaskChangeOneSourceUnitCls item)
		{
            if (!this._UnitList.Exists(i => i.IsEqualTo(item)))
                this._UnitList.Add(item);
		}
		#endregion
		#region LOAD & SAVE Z/DO DATABÁZE
		/// <summary>
		/// Metoda vytvoří, naplní a vrátí data o jednom WorkPass na základě dat načtených z databáze.
		/// </summary>
		/// <param name="record">Záznam s daty, načtený z databáze</param>
		/// <param name="taskItem">Zaznam Parent</param>
		/// <returns></returns>
		internal static WorkPassCls LoadFromDb(PlanUnitCPassCls record, CapacityPlanWorkItemCls taskItem)
		{
			decimal qtyFinal = (record.QtyFinal.IsNull ? 0M : record.QtyFinal.Value);
			decimal qtyRequired = (record.QtyRequired.IsNull ? 0M : record.QtyRequired.Value);

            WorkPassCls pass = WorkPassCls.Create(taskItem, qtyRequired, qtyFinal, taskItem.UnitCount);

			pass.RecordNumber = record.RecordNumber;               // číslo nonsubjektu uložím do dat v plánovacím procesu

			return pass;
		}
		/// <summary>
		/// Přidá do interního soupisu jednu položku času. Soupis netřídí, nastaví se příznak Unsorted.
		/// Třídění se provede v případě potřeby.
		/// </summary>
		/// <param name="timeItem"></param>
		internal void DataLoadAddWorkTime(WorkTimeCls time)
		{
			if (time == null) return;
			this._WorkTimeLst.Add(time);
			this._WorkTimeListUnsorted = true;            // Příznak, že soupis časů _WorkTimeList není setříděn
		}
		/// <summary>
		/// Metoda zajistí finalizaci proměnných po načtení z databáze.
		/// Provede se přepočet časů fází na hlavičce (this) podle položek WorkTime.
		/// </summary>
		internal void DataLoadFinalize()
		{
			// Setřídit položky WorkTime podle času (kdoví v jakém pořadí přišly z databáze):
			if (this._WorkTimeLst.Count > 1)
				this._WorkTimeLst.Sort(WorkTimeCls.CompareByBeginPhase);

			// Přepočítat časy fází a čas celého průchodu z jednotlivých položek WorkTime:
			this._RecalcPassTime(this.AxisData.DatePlanTime);

			// Přepočítat chybějící kapacitu fází:
			this._RecalcMissingTime();

			// Stav registrace kapacit = registrováno:
			this._RegisterState = CapacityRegisterState.Initialized | CapacityRegisterState.Registered;
		}
		/// <summary>
		/// Pokusí se najít a vrátit průchod daného čísla ID / GID.
		/// Pokud nenajde, vrací null.
		/// </summary>
		/// <param name="timeGID">Klíč hledaného času</param>
		/// <returns></returns>
		internal WorkTimeCls FindWorkTime(GID timeGID)
		{
			if (timeGID.ClassNumber != Constants.ClassNumberTime) return null;
			int timeID = timeGID.RecordNumber;
			return this._WorkTimeLst.Find(delegate(WorkTimeCls t) { return (t.TimeID == timeID); });
		}
		/// <summary>
		/// Metoda vynuluje svoji hodnotu RecordNumber a totéž provede pro svoje přímé vnitřní členy.
		/// </summary>
		internal override void StoreEmptyRecordNumber()
		{
			base.StoreEmptyRecordNumber();
			foreach (WorkTimeCls time in this._WorkTimeLst)
				time.StoreEmptyRecordNumber();
		}
		/// <summary>
		/// Metoda uloží veškerá svoje data do záznamu PlanUnitCPassCls a jeho prostřednictvím do databáze.
		/// Uloží i data ze záznamů podřízených tříd.
		/// </summary>
		internal override void SaveRecord()
		{
			this._SaveRecordPass();
			foreach (WorkTimeCls time in this._WorkTimeLst)
				time.SaveRecord();
		}
		/// <summary>
		/// Metoda uloží veškerá svoje data do záznamu PlanUnitCPassCls a jeho prostřednictvím do databáze.
		/// Neukládá podřízené třídy.
		/// </summary>
		private void _SaveRecordPass()
		{
			using (PlanUnitCPassCls record = new PlanUnitCPassCls())
			{
				record.QtyFinal = this.QtyFinal;
				record.QtyRequired = this.QtyRequired;
				record.PlanUnitCTask = this.Parent.RecordNumber;
				this.SaveViaRecord(record);
			}
		}
		#endregion
        #region DIAGNOSTIKA
        /// <summary>
        /// Přidá svoje data do diagnostiky (pár věcí z hlavičky, časy, jednotky)
        /// </summary>
        /// <param name="planningDataCls"></param>
        /// <param name="rowPrefix"></param>
        internal void AddToDiagnostic(PlanningDataCls planningDataCls, string title, string rowPrefix)
        {
            Steward.Diagnostic.SubTitleAdd(title);
            string pref = (rowPrefix == null ? "" : rowPrefix);
            Steward.Diagnostic.InfoAddLine(pref + "PASS ID=" + this.PassID.ToString() + "; QtyRequired=" + this.QtyRequired.ToString() + "; Time=" + this.TimeWork.ToString());
            for (int t = 0; t < this._WorkTimeLst.Count; t++)
            {
                WorkTimeCls time = this._WorkTimeLst[t];
                time.AddToDiagnostic(planningDataCls, null, pref + " time[" + t.ToString() + "]: ");
            }
        }
        #endregion
    }
	#endregion
	#region CLASS WorkTimeCls : data jednoho uceleného času v rámci práce na jednom paralelním průchodu.
	/// <summary>
	/// WorkTimeCls : data jednoho uceleného času v rámci práce na jednom paralelním průchodu. 
	/// Obsahuje informace o všech použitých kapacitních plánovacích jednotkách.
	/// </summary>
	public class WorkTimeCls : PlanItemCls<WorkPassCls>
	{
		#region KONSTRUKTORY
		/// <summary>
		/// Konstruktor = pro konkrétního Parenta, kterým je instance paralelního průchodu
		/// </summary>
		/// <param name="parent"></param>
		private WorkTimeCls(WorkPassCls parent)
			: base(parent)
		{
			this._WorkUnitLst = new List<WorkUnitCls>();
		}
		/// <summary>
		/// Statický konstruktor
		/// </summary>
		/// <param name="parent">Parent = paralelní průchod</param>
		/// <param name="args">Data pro tvorbu objektu</param>
		/// <returns></returns>
		internal static WorkTimeCls Create(WorkPassCls parent, TaskRegDataAddTimeArgs args)
		{
			WorkTimeCls time = new WorkTimeCls(parent);
			time._Phase = args.Phase;
			time._WorkTime = args.Time;
			time._WorkCapacity = args.WorkCapacity;
			time._StandardRatio = args.StandardRatio;
			return time;
		}
		/// <summary>
		/// Vizualizace
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			return "{Time=" + this.WorkTime + "; Phase=" + this.Phase + "; Ratio=" + this.StandardRatio + "; UnitCount=" + this.WorkUnitLst.Count + "}";
		}
		/// <summary>
		/// Diagnostický text
		/// </summary>
		public string DiagnosticText { get { return "TimeID=" + this.ItemID + "; Time=" + this.WorkTime + "; Phase=" + this.Phase + "; UnitCount=" + this._WorkUnitLst.Count.ToString(); } }
		#endregion
		#region PROMĚNNÉ A PROPERTY
		/// <summary> { get; } GID času = klíč z čísla pracovní třídy (Constants.ClassNumberTime = 0x10003) a čísla záznamu TimeID</summary>
		internal GID TimeGID { get { return new GID(Constants.ClassNumberTime, this.ItemID); } }
		/// <summary> { get; } ID Time = společný čas jednotky práce</summary>
		internal int TimeID { get { return this.ItemID; } }
		/// <summary> { get; } ID Pass = jeden z paralelních průběhů</summary>
		internal int PassID { get { return this.Parent.PassID; } }
		/// <summary> { get; } ID Task = operace</summary>
		internal int TaskID { get { return this.Parent.TaskID; } }
		/// <summary> { get; } Číslo DB záznamu tasku = operace</summary>
		public int TaskRecordNumber { get { return this.Parent.TaskRecordNumber; } }
		/// <summary> { get; } Data úkolu = operace. Jedna operace může mít 1 i víc průchodů = Pass.</summary>
		public CapacityPlanWorkItemCls TaskData { get { return this.Parent.TaskData; } }
		/// <summary> { get; } ID Axis = materiálová osa</summary>
		internal int AxisID { get { return this.Parent.AxisID; } }
		/// <summary> { get; } Data osy S = materiálová osa</summary>
		internal MaterialPlanAxisItemCls AxisData { get { return this.Parent.AxisData; } }
		/// <summary>
		/// Aktuální fáze, prováděná v tomto čase
		/// </summary>
		internal OperationPhase Phase { get { return this._Phase; } }
		private OperationPhase _Phase;
		/// <summary>
		/// Časový interval, po který se v tomto časovém úseku pracuje.
		/// Jednotlivé úseky Time v rámci jednoho WorkPass na sebe navazují, nepřekrývají se.
		/// Časový úsek WorkTimeCls.Time nemůže přesahovat přes hranu stavu kapacit, z níž čerpá:
		/// pokud čerpáme z více (i těsně sousedních) stavů kapacit, existuje pro každý stav kapacit jeho odpovídající záznam Time.
		/// </summary>
		internal TimeRange WorkTime { get { return this._WorkTime; } }
		private TimeRange _WorkTime;
		/// <summary> { get; } Druh tasku, určuje cílový druh čerpaných kapacit (Reál, Plán, Simulace)</summary>
		internal CapacityUsageType CapacType { get { return this.Parent.CapacType; } }
		///<summary> { get; } Množství finálního výrobku celkem - toto množství se neplánuje, jen slouží k určování potřebnosti alokace fáze MoveTime, TBC, TEC.</summary>
		public decimal QtyFinal { get { return this.Parent.QtyFinal; } }
		///<summary> { get; } Množství chybějící, toto se bere do plánu. Fyzicky vyráběný počet na tomto tasku.</summary>
		public decimal QtyRequired { get { return this.Parent.QtyRequired; } }
		/// <summary> { get; }
		/// Příznak, zda operace je časově fixovaná (true = fixovaná / false = volná)
		/// </summary>
		internal bool IsFixedTask { get { return this.Parent.IsFixedTask; } set { this.Parent.IsFixedTask = value; } }
		/// <summary>
		/// Poměr využití kapacit v tomto čase.
		/// Nemůže být 0 a záporný (to by se nic nevyrobilo ani za sto let).
		/// Hodnota 1 = 100% plnění norem.
		/// Hodnota 0.75 znamená 75% plnění norem, tzn. za 1 hodinu fyzického času (WorkTime) se odpracuje 0.75 * 1h = 45 minut z potřebné kapacity úkolu.
		/// </summary>
		internal decimal StandardRatio { get { return this._StandardRatio; } }
		private decimal _StandardRatio;
		/// <summary>
		/// Reálně odčerpaná kapacita z kapacitních jednotek v tomto časovém úseku.
		/// Je to fyzický čas intervalu (WorkTime).
		/// </summary>
		internal decimal WorkCapacity { get { return this._WorkCapacity; } }
		private decimal _WorkCapacity;
		/// <summary>
		/// Kapacita, kterou tato práce odebírá ze zadaného pracovního úkolu (průchodu).
		/// Je to fyzický čas (WorkTime) v sekundách krát koeficient StandardRatio.
		/// </summary>
		internal decimal PassCapacity { get { return (decimal)this.WorkTime.Seconds * this.StandardRatio; } }
		/// <summary>
		/// Veřejný (public) read-only souhrn pracovních jednotek v tomto čase.
		/// Do tohoto listu není možno přidávat / odebírat položky. Je to ReadOnlyCollection.
		/// Je možno ji používat jako normální List, vyjma jakýchkoli modifikací.
		/// </summary>
		public ReadOnlyCollection<WorkUnitCls> WorkUnitList { get { return new ReadOnlyCollection<WorkUnitCls>(this._WorkUnitLst); } }
		/// <summary>
		/// Souhrn pracovních jednotek v tomto čase. Jde o internal přímou referenci na privátní List, neprovádí se read-only konverze.
		/// </summary>
		internal List<WorkUnitCls> WorkUnitLst { get { return this._WorkUnitLst; } }
		private List<WorkUnitCls> _WorkUnitLst;
		#endregion
		#region KOMPARÁTOR
		/// <summary>
		/// Komparátor podle data počátku (WorkTime.Begin), ASCENDNIG, 
		/// nižší hodnota dříve,
		/// a při stejném okamžiku počátku podle pořadí fáze Phase (None - MoveTime - TBC - TAC - TEC).
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		internal static int CompareByBeginPhase(WorkTimeCls a, WorkTimeCls b)
		{
			int cmp = a._WorkTime.Begin.CompareTo(b._WorkTime.Begin);
			if (cmp != 0) return cmp;
			cmp = ((int)a._Phase).CompareTo((int)b._Phase);
			return cmp;
		}
		/// <summary>
		/// Komparátor podle data počátku (WorkTime.Begin), DESCENDING, 
		/// vyšší hodnota dříve,
		/// a při stejném okamžiku počátku podle reverního pořadí fáze Phase (TEC - TAC - TBC - MoveTime - None).
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		internal static int CompareByBeginPhaseDesc(WorkTimeCls a, WorkTimeCls b)
		{
			return CompareByBeginPhase(b, a);
		}
		#endregion
		#region ČTENÍ DAT O JEDNOTKÁCH PRÁCE A O VZTAZÍCH MEZI NIMI PRO JEJICH ZOBRAZOVÁNÍ
		/// <summary>
		/// Obsahuje true, pokud tato jednotka času obsahuje alespoň jednu položku práce WorkUnit, 
		/// kterou bude ochotna předat do soupisu v metodě CreateDataRelation().
		/// </summary>
		internal bool ContainAnyWork { get { return (this._WorkUnitLst.Count > 0); } }
		/// <summary>
		/// Vrátí true, pokud někde v tomto WorkTime existuje WorkUnit s daným klíčem.
		/// </summary>
		/// <param name="workGID">Klíč hledané položky WorkUnit</param>
		/// <returns></returns>
		internal bool ContainWorkUnit(GID workGID)
		{
			if (workGID.ClassNumber != Constants.ClassNumberWork) return false;
			return this._WorkUnitLst.Exists(delegate(WorkUnitCls u) { return (u.WorkID == workGID.RecordNumber); });
		}
		/// <summary>
		/// Do předaného soupisu workUnitList přidá všechny své záznamy WorkUnit.
		/// </summary>
		/// <param name="workUnitList"></param>
		internal void CreateWorkUnitList(ref List<WorkUnitCls> workUnitList)
		{
			workUnitList.AddRange(this._WorkUnitLst);
		}
		/// <summary>
		/// Naplní do daného seznamu WorkUnit ze sebe (ze svých časů), dané kategorie.
		/// </summary>
		/// <param name="category"></param>
		/// <param name="workUnitList"></param>
		internal void GetWorkUnitListOfCategory(CapacitySourceCategory category, ref List<WorkUnitCls> workUnitList)
		{
			foreach (WorkUnitCls workUnit in this._WorkUnitLst)
			{
				if (category == CapacitySourceCategory.AllCategory || workUnit.Category == category)
					workUnitList.Add(workUnit);
			}
		}
		/// <summary>
        /// Souhrn klíčů (ID): this + vnořených Work
        /// </summary>
        internal string DebugKeys
        {
            get
            {
                string text = "Time " + this.TimeID.ToString();
                if (this._WorkUnitLst.Count > 0)
                {
                    string prefix = "; UnitList = {";
                    foreach (WorkUnitCls workUnit in this._WorkUnitLst)
                    {
                        text += prefix + workUnit.DebugKeys;
                        prefix = ", ";
                    }
                    text += "}";
                }
                return text;
            }
        }
        /// <summary>
		/// Do předaného soupisu dataRelationList přidá záznamy o položkách a vzájemných paralelních vztazích mezi položkami za tento jednotlivý čas.
		/// Tato metoda neřeší vztahy mezi dvěma časy.
		/// </summary>
		/// <param name="dataRelationList">Ref výstup pro soupis položek Work a Relation.</param>
		internal void CreateDataRelation(ref List<DataRelation> dataRelationList)
		{
			WorkUnitCls lastUnit = null;
			foreach (WorkUnitCls workUnit in this._WorkUnitLst)
			{
				// 1. Informace o samotné jednotce WorkUnit:
				dataRelationList.Add(PlanningDataCls.GetWorkUnitRelItemData(workUnit));

				// 2. Informace o paralelním vztahu:
				if (lastUnit != null)
					dataRelationList.Add(PlanningDataCls.GetWorkUnitRelRelation(lastUnit, workUnit, ElementRelationOrientation.Parallel));

				// 3. Průběžná paměť předešlé jednotky práce:
				lastUnit = workUnit;
			}
			lastUnit = null;
		}
		/// <summary>
		/// Metoda najde a do dodaného seznamu naplní použité kapacitní jednotky za všechny kategorie, včetně jejich použité kapacity.
		/// Provede ze všech časů.
		/// </summary>
		/// <param name="usedDict">Průběžně doplňovaný seznam KPJ a jejich časů</param>
		internal void CreatePlanUnitList(ref Dictionary<int, decimal> usedDict)
		{
			this.CreatePlanUnitList(CapacitySourceCategory.AllCategory, ref usedDict);
		}
		/// <summary>
		/// Metoda najde a do dodaného seznamu naplní použité kapacitní jednotky na dané kategorii, včetně jejich použité kapacity.
		/// Provede ze všech časů.
		/// </summary>
		/// <param name="sourceCategory">Požadovaný typ zdroje (např. pracoviště)</param>
		/// <param name="usedDict">Průběžně doplňovaný seznam KPJ a jejich časů</param>
		internal void CreatePlanUnitList(CapacitySourceCategory sourceCategory, ref Dictionary<int, decimal> usedDict)
		{
			foreach (WorkUnitCls unit in this._WorkUnitLst)
			{
				if (sourceCategory == CapacitySourceCategory.AllCategory || unit.Category == sourceCategory)
				{
					if (usedDict.ContainsKey(unit.CapacityUnit))
						usedDict[unit.CapacityUnit] += unit.UseCapacity;
					else
						usedDict.Add(unit.CapacityUnit, unit.UseCapacity);
				}
			}
		}
		/// <summary>
		/// Metoda vrátí seznam datapointerů na všechny WorkUnity (ze všech Pass - Time - Unit).
		/// V dataPointeru je řádek = kapacitní jednotka, a element = WorkUnit.
		/// </summary>
		/// <returns></returns>
		internal void CreateAllWorkUnitPointers(ref List<DataPointerStr> pointerList)
		{
			foreach (WorkUnitCls workUnit in this._WorkUnitLst)
				pointerList.Add(new DataPointerStr(new GID(PlanUnitCCls.ClassNr, workUnit.CapacityUnit), workUnit.WorkGID));
		}
		/// <summary>
		/// Do předaného soupisu dataRelationList přidá záznamy o vzájemných vztazích 
		/// mezi položkami dvou sousedních časů (lastTime a currTime).
		/// </summary>
		/// <param name="lastTime">Předchozí jednotka času</param>
		/// <param name="currTime">Aktuální jednotka času</param>
		/// <param name="relation">Vztah z položek předchozí jednotky času na položky aktuální</param>
		internal static void CreateDataRelation(WorkTimeCls lastTime, WorkTimeCls currTime, ElementRelationOrientation relation, ref List<DataRelation> dataRelationList)
		{
			if (lastTime == null || currTime == null) return;
			int lastCount = lastTime.WorkUnitLst.Count;
			int currCount = currTime.WorkUnitLst.Count;
			if (lastCount == 0 || currCount == 0) return;

			bool reverse = (currTime.WorkUnitLst.Count > lastTime.WorkUnitLst.Count);                    // true, pokud currTime má víc položek, než lastTime
			List<_WorkUnitTempCls> leftList = (!reverse ? lastTime : currTime)._GetWorkUnitTempList();   // Seznam _WorkUnitTempCls z času, který má položek více
			List<_WorkUnitTempCls> rightList = (reverse ? lastTime : currTime)._GetWorkUnitTempList();   // Seznam _WorkUnitTempCls z času, který má položek méně

			for (int mode = 1; mode <= 6; mode++)
			{	// Máme N režimů vyhledání párového prvku:
				_WorkUnitFindMode findMode = (_WorkUnitFindMode)mode;         // Zde je konverze možná, enum má exaktně číslované typy.
				bool isUnpair = false;                   // Pokud nějaký prvek nenajde svůj párový prvek, nastavíme na true a po dokončení smyčky left pojedeme další režim.
				int rightNext = 0;                       // Využívá se v režimu 6
				for (int leftIndex = 0; leftIndex < leftList.Count; leftIndex++)
				{	// Pro každý prvek vlevo hledáme prvek vpravo:
					_WorkUnitTempCls leftData = leftList[leftIndex];
					if (leftData.IsPaired) continue;     // Ty prvky vlevo, které jsou už spárované, mohu přeskočit.

					// Najdu (jedním ze šesti režimů) vhodný prvek vpravo:
					_WorkUnitTempCls rightData = null;
					switch (findMode)
					{
						case _WorkUnitFindMode.ExactMatch:
							// Nejprve bychom rádi našli prvek úplně shodný a volný:
							rightData = rightList.Find(delegate(_WorkUnitTempCls rightDt)
								{ return (rightDt.CapacitySource == leftData.CapacitySource && rightDt.CapacityUnit == leftData.CapacityUnit && rightDt.CapacityDesk == leftData.CapacityDesk && !rightDt.IsPaired); });
							break;
						case _WorkUnitFindMode.EqualUnit:
							// V druhé řadě by měl být prvek shodný, ale budeme ignorovat číslo stolu:
							rightData = rightList.Find(delegate(_WorkUnitTempCls rightDt)
								{ return (rightDt.CapacitySource == leftData.CapacitySource && rightDt.CapacityUnit == leftData.CapacityUnit && !rightDt.IsPaired); });
							break;
						case _WorkUnitFindMode.EqualSource:
							// Ve třetí řadě by měl mít prvek shodný kapacitní zdroj, když už ne konkrétní jednotku, a měl by být volný:
							rightData = rightList.Find(delegate(_WorkUnitTempCls rightDt)
								{ return (rightDt.CapacitySource == leftData.CapacitySource && !rightDt.IsPaired); });
							break;
						case _WorkUnitFindMode.EqualCategory:
							// No tak alespoň stejnou kategorii:
							rightData = rightList.Find(delegate(_WorkUnitTempCls rightDt)
							{ return (rightDt.Category == leftData.Category && !rightDt.IsPaired); });
							break;
						case _WorkUnitFindMode.AnyFree:
							// No tak alespoň jakoukoli volnou jednotku:
							rightData = rightList.Find(delegate(_WorkUnitTempCls rightDt)
								{ return (!rightDt.IsPaired); });
							break;
						case _WorkUnitFindMode.Any:
							// Beru kteroukoli, všechny vpravo už jsou obsazené, takže najdu první vhodný:
							rightData = rightList[rightNext];
							rightNext++;
							if (rightNext >= rightList.Count)
								rightNext = 0;
							break;
					}

					if (rightData == null)
					{
						isUnpair = true;
						continue;              // Jdeme na další prvek vlevo.
					}

					// Obsadíme položku vlevo i vpravo a vytvoříme pár:
					leftData.IsPaired = true;
					rightData.IsPaired = true;
					if (!reverse)
						dataRelationList.Add(PlanningDataCls.GetWorkUnitRelRelation(leftData.WorkUnit, rightData.WorkUnit, relation));
					else
						dataRelationList.Add(PlanningDataCls.GetWorkUnitRelRelation(rightData.WorkUnit, leftData.WorkUnit, relation));
				}
				// Pokud neexistuje žádná položka nespárovaná, jsou všechny OK a skončíme:
				if (!isUnpair)
					break;
			}
		}
		/// <summary>
		/// Vygeneruje a vrátí seznam prvků třídy _WorkUnitTempCls, s prvky odvozenými z permanentních prvků WorkUnitCls, z this.WorkUnitList
		/// </summary>
		/// <returns></returns>
		private List<_WorkUnitTempCls> _GetWorkUnitTempList()
		{
			List<_WorkUnitTempCls> tempList = new List<_WorkUnitTempCls>();
			foreach (WorkUnitCls workUnit in this.WorkUnitLst)
				tempList.Add(new _WorkUnitTempCls(workUnit));
			return tempList;
		}
		/// <summary>
		/// Pokusí se najít a vrátit element práce (WorkUnit) nejvhodnější k dané původní lokaci.
		/// Metoda hledá jen v rámci svých WorkUnit nejpřesnější shodu v ukazatelích Unit, Source, Category.
		/// Metoda neřeší OperationKey, Phase, Time.
		/// Metoda vrátí null jen tehdy, když tento objekt nemá žádnou WorkUnit.
		/// </summary>
		/// <param name="location">Lokátor pozice. Popisuje fázi a relativní čas uvnitř fáze, popisuje typ kapacity (kategorii, zdroj, jednotku).</param>
		/// <returns></returns>
		internal WorkUnitCls FindUnitItem(WorkUnitLocationCls location)
		{
			return _FindUnitItem(location.Category, location.CapacitySource, location.CapacityUnit, location.CapacityDesk);
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="category">Kategorie zdroje kapacit</param>
		/// <param name="capacitySource">Obecný zdroj kapacit</param>
		/// <param name="capacityUnit">Kapacitní jednotka</param>
		/// <param name="capacityDesk">Kapacitní stůl, index</param>
		/// <returns></returns>
		private WorkUnitCls _FindUnitItem(CapacitySourceCategory category, int capacitySource, int capacityUnit, int capacityDesk)
		{
			if (this._WorkUnitLst.Count == 0) return null;             // Kde nic není, tam nic nenajdu !
			for (int mode = 1; mode <= 6; mode++)
			{	// Máme N režimů vyhledání párového prvku:
				_WorkUnitFindMode findMode = (_WorkUnitFindMode)mode;  // Zde je konverze možná, enum má exaktně číslované typy.
				WorkUnitCls findUnit = null;
				switch (findMode)
				{
					case _WorkUnitFindMode.ExactMatch:
						// Nejprve bychom rádi našli prvek úplně shodný:
						findUnit = this._WorkUnitLst.Find(delegate(WorkUnitCls w)
						{ return (w.CapacitySource == capacitySource && w.CapacityUnit == capacityUnit && w.CapacityDesk == capacityDesk); });
						break;
					case _WorkUnitFindMode.EqualUnit:
						// V druhé řadě by měl být prvek shodný, ale budeme ignorovat číslo stolu:
						findUnit = this._WorkUnitLst.Find(delegate(WorkUnitCls w)
						{ return (w.CapacitySource == capacitySource && w.CapacityUnit == capacityUnit); });
						break;
					case _WorkUnitFindMode.EqualSource:
						// Ve třetí řadě by měl mít prvek shodný kapacitní zdroj, když už ne konkrétní jednotku:
						findUnit = this._WorkUnitLst.Find(delegate(WorkUnitCls w)
						{ return (w.CapacitySource == capacitySource); });
						break;
					case _WorkUnitFindMode.EqualCategory:
						// No tak alespoň stejnou kategorii:
						findUnit = this._WorkUnitLst.Find(delegate(WorkUnitCls w)
						{ return (w.Category == category); });
						break;
					case _WorkUnitFindMode.AnyFree:
						// No tak alespoň jakoukoli jednotku:
					case _WorkUnitFindMode.Any:
						// Beru kteroukoli, všechny vpravo už jsou obsazené, takže najdu první vhodný:
						findUnit = this._WorkUnitLst[0];                 // Jeden je tam vždycky (viz podmínka na prvním řádku)
						break;
				}
				if (findUnit != null)
					return findUnit;
			}
			return null;
		}
		#region PRIVATE CLASS _WorkUnitTempCls : pracovní třída pro přípravu vztahů mezi položkami WorkUnitCls dvou sousedních WorkTimeCls
		/// <summary>
		/// _WorkUnitTempCls : pracovní třída pro přípravu vztahů mezi položkami WorkUnitCls dvou sousedních WorkTimeCls
		/// </summary>
		private class _WorkUnitTempCls
		{
			internal _WorkUnitTempCls(WorkUnitCls workUnit)
			{
				this.Category = workUnit.Category;
				this.CapacitySource = workUnit.CapacitySource;
				this.CapacityUnit = workUnit.CapacityUnit;
				this.CapacityDesk = workUnit.CapacityDesk;
				this.IsPaired = false;
				this.WorkUnit = workUnit;
			}
			internal CapacitySourceCategory Category;
			internal int CapacitySource;
			internal int CapacityUnit;
			internal int CapacityDesk;
			internal float FragmentBegin;
			internal bool IsPaired;
			internal WorkUnitCls WorkUnit;
		}
		/// <summary>
		/// Režimy hledání odpovídající jednotky WorkUnit
		/// </summary>
		private enum _WorkUnitFindMode : int
		{
			/// <summary>1. Nejprve bychom rádi našli prvek úplně shodný a volný</summary>
			ExactMatch = 1,
			/// <summary>2. V druhé řadě by měl být prvek shodný, ale budeme ignorovat číslo stolu</summary>
			EqualUnit = 2,
			/// <summary>3. Ve třetí řadě by měl mít prvek shodný kapacitní zdroj, když už ne konkrétní jednotku, a měl by být volný</summary>
			EqualSource = 3,
			/// <summary>4. No tak alespoň stejnou kategorii</summary>
			EqualCategory = 4,
			/// <summary>5. No tak alespoň jakoukoli volnou jednotku</summary>
			AnyFree = 5,
			/// <summary>6. Beru kteroukoli jednotku, všechny vpravo už jsou obsazené, takže najdu první vhodný</summary>
			Any = 6
		}
		#endregion
		/// <summary>
		/// Do předaného soupisu dataRelationList přidá záznamy o položkách a vztazích mezi položkami za tento pracovní průchod.
		/// </summary>
		/// <param name="lastTime">Inp: Předcházející čas, na jeho WorkList napojujeme vztahy / Out: poslední WorkTime, jeho WorkUnit budeme napojovat na sousední WorkPass</param>
		/// <param name="homeWorkId">Počínaje daným WorkID, nebo 0 = počínaje prvním Work</param>
		/// <param name="relation">Vztah z předchozího času</param>
		/// <param name="dataRelationList"></param>
		internal void CreateDataRelation(ref WorkTimeCls lastTime, int homeWorkId, ElementRelationOrientation relation, ref List<DataRelation> dataRelationList)
		{
			// Měli bychom v tomto čase (this) najít výchozí pložku práce WorkUnit?
			bool isHomeWork = (homeWorkId != 0);
			WorkUnitCls homeWork = null;
			if (isHomeWork)
			{	// Asi bychom ji měli najít, zkusíme to:
				homeWork = this.WorkUnitLst.Find(delegate(WorkUnitCls s) { return (s.WorkID == homeWorkId); });
				if (homeWork != null)
					// Našli jsme => dáme ji do výsledku jako první:
					dataRelationList.Add(PlanningDataCls.GetWorkUnitRelItemData(homeWork));
				else
					// Nenalši => shodíme si příznak, ať nás neplete.
					isHomeWork = false;
			}

			// Příznak, že máme k dispozici soupis předchozích WorkUnits:
			bool isRelatedList = (lastTime != null && lastTime.WorkUnitLst.Count > 0);

			// Pro všechny položky práce v this čase:
			WorkUnitCls syncWork = null;         // Předešlá položka práce v jednom společném čase, pro navázání do vztahu Parallel
			foreach (WorkUnitCls workItem in this.WorkUnitLst)
			{
				// 1. Položky WorkItem v čase this (WorkTime),
				// 2. Vzájemné vztahy mezi položkami v jednom Time (vztah = Parallel):
				// Pokud mám předanou "výchozí položku práce", a zrovna teď jsem na ni narazil, tak ji nevkládám do soupisu:
				if (!(isHomeWork && workItem.WorkID == homeWork.WorkID))
				{	// Aktuální položka nikdy není rovna homeWork. 
					//   Ta se přidává separátně ještě před touto smyčkou.
					//   Vztahy na položku homeWork se řeší hvězdicově: 
					//     všechny ostatní z jednotlivých okolních workItem se vážou na ní, viz paralelní vztahy ad a)
					// Aktuální položku práce přidám do soupisu jako Item:
					dataRelationList.Add(PlanningDataCls.GetWorkUnitRelItemData(workItem));

					// Paralelní vztah mezi touto položkou a položkou syncWork nebo homeWork:
					if (isHomeWork)
						// a) jsme v režimu, kdy existuje "výchozí položka práce" => všechny ostatní položky vážeme k té výchozí:
						dataRelationList.Add(PlanningDataCls.GetWorkUnitRelRelation(homeWork, workItem, ElementRelationOrientation.Parallel));
					else if (syncWork != null)
						// b) jsme v režimu, kdy neexistuje "výchozí položka práce" => položky postupně vážeme k předchozí položce:
						dataRelationList.Add(PlanningDataCls.GetWorkUnitRelRelation(syncWork, workItem, ElementRelationOrientation.Parallel));

					// Průběžná registrace položky syncWork : zaregistrujeme si předcházející položku:
					if (!isHomeWork) syncWork = workItem;
				}

				// 3. Vztahy mezi aktuální položkou workItem a položkami ze sousedního času Time (ze seznamu workListPrev):
				//    Toto se týká i položky homeWork (pokud je zadaná)
				if (isRelatedList)
				{
					switch (relation)
					{
						case ElementRelationOrientation.NextWork:
							// NextWork = položky seznamu this.WorkUnitList jsou Next, položky z lastTime.WorkUnitList jsou Prev:
							foreach (WorkUnitCls relatedItem in lastTime.WorkUnitLst)
								dataRelationList.Add(PlanningDataCls.GetWorkUnitRelRelation(relatedItem, workItem, relation));
							break;
						case ElementRelationOrientation.PrevWork:
							// PrevWork = položky seznamu this.WorkUnitList jsou Prev, položky z lastTime.WorkUnitList jsou Next:
							foreach (WorkUnitCls relatedItem in lastTime.WorkUnitLst)
								dataRelationList.Add(PlanningDataCls.GetWorkUnitRelRelation(workItem, relatedItem, relation));
							break;
					}
				}
			}
		}
		/// <summary>
		/// Získá OuterPoint z tohoto času.
		/// OuterPoint je ukazatel na jednotku práce (WorkUnit), která v rámci tohoto času ukazuje na plánovací jednotku,
		/// která je typu Workplace (nebo Profession, nebo Resource, v tomto pořadí) a má nejnižší číslo plánovací jednotky
		/// </summary>
		/// <returns></returns>
		internal DataPointerStr GetOuterPointer()
		{
			// Najít nejlepší jednotku:
			WorkUnitCls bestUnit = null;
			for (int i = 0; i < this._WorkUnitLst.Count; i++)
			{
				WorkUnitCls workUnit = this._WorkUnitLst[i];
				if (bestUnit == null)
					bestUnit = workUnit;
				else if (WorkUnitCls.CompareBestOf(workUnit, bestUnit) < 0)
					bestUnit = workUnit;
			}

			// Výsledek má být DataPointer na nalezenou (?) jednotku:
			DataPointerStr result = DataPointerStr.Empty;
			if (bestUnit != null)
				result = Planning.ProcessData.PlanningDataCls.GetDataPointer(bestUnit);
			return result;
		}
		#endregion
        #region DIAGNOSTIKA
        /// <summary>
        /// Přidá svoje data do diagnostiky (pár věcí z hlavičky, jednotky)
        /// </summary>
        /// <param name="planningDataCls"></param>
        /// <param name="rowPrefix"></param>
        internal void AddToDiagnostic(PlanningDataCls planningDataCls, string title, string rowPrefix)
        {
            Steward.Diagnostic.SubTitleAdd(title);
            string pref = (rowPrefix == null ? "" : rowPrefix);
            Steward.Diagnostic.InfoAddLine(pref + "TIME ID=" + this.TimeID.ToString() + "; Phase=" + this.Phase.ToString() + "; Time=" + this.WorkTime.ToString());
            for (int w = 0; w < this._WorkUnitLst.Count; w++)
            {
                WorkUnitCls work = this._WorkUnitLst[w];
                work.AddToDiagnostic(planningDataCls, null, pref + " work[" + w.ToString() + "]: ");
            }
        }
		#endregion
		#region LOAD & SAVE Z/DO DATABÁZE
		/// <summary>
		/// Metoda vytvoří, naplní a vrátí data o jednom WorkTime na základě dat načtených z databáze.
		/// </summary>
		/// <param name="record"></param>
		/// <param name="passItem"></param>
		/// <returns></returns>
		internal static WorkTimeCls LoadFromDb(PlanUnitCTimeCls record, WorkPassCls passItem)
		{
			TimeRange workTime = new TimeRange(record.TimeBegin, record.TimeEnd);
			if (workTime.IsEmpty) return null;

			WorkTimeCls time = new WorkTimeCls(passItem);

			time.RecordNumber = record.RecordNumber;               // číslo nonsubjektu uložím do dat v plánovacím procesu

			time._Phase = PlanningComponentCls.SqlInt16ToOperationPhase(record.Phase);
			time._WorkTime = workTime;
			time._WorkCapacity = (record.WorkCapacity.IsNull ? 0M : record.WorkCapacity.Value);
			time._StandardRatio = (record.StandardRatio.IsNull ? 0M : record.StandardRatio.Value);
			
			return time;
		}
		/// <summary>
		/// Přidá do interního seznamu pracovních jednotek další jednotku.
		/// </summary>
		/// <param name="unitItem"></param>
		internal void AddWorkUnit(WorkUnitCls unitItem)
		{
			this._WorkUnitLst.Add(unitItem);
		}
		/// <summary>
		/// Metoda vynuluje svoji hodnotu RecordNumber a totéž provede pro svoje přímé vnitřní členy.
		/// </summary>
		internal override void StoreEmptyRecordNumber()
		{
			base.StoreEmptyRecordNumber();
			foreach (WorkUnitCls unit in this._WorkUnitLst)
				unit.StoreEmptyRecordNumber();
		}
		/// <summary>
		/// Metoda uloží veškerá svoje data do záznamu PlanUnitCTimeCls a jeho prostřednictvím do databáze.
		/// Uloží i data ze záznamů podřízených tříd.
		/// </summary>
		internal override void SaveRecord()
		{
			this._SaveRecordTime();
			foreach (WorkUnitCls unit in this._WorkUnitLst)
				unit.SaveRecord();
		}
		/// <summary>
		/// Metoda uloží veškerá svoje data do záznamu PlanUnitCTimeCls a jeho prostřednictvím do databáze.
		/// Neukládá podřízené třídy.
		/// </summary>
		private void _SaveRecordTime()
		{
			using (PlanUnitCTimeCls record = new PlanUnitCTimeCls())
			{
				record.Phase = PlanningComponentCls.OperationPhaseToSqlInt16(this.Phase);
				record.TimeBegin = this.WorkTime.Begin;
				record.TimeEnd = this.WorkTime.End;
				record.WorkCapacity = this.WorkCapacity;
				record.StandardRatio  = this.StandardRatio;
				record.PlanUnitCPass = this.Parent.RecordNumber;
				this.SaveViaRecord(record);
			}
		}
		#endregion
	}
	#endregion
	#region CLASS WorkUnitCls : data jednotlivého pracovního výkonu (čas x kapacitní jednotka).
	/// <summary>
	/// WorkUnitCls : data jednotlivého pracovního výkonu (čas x kapacitní jednotka).
	/// Jde o nejnižší úroveň detailu v systému využití kapacit.
	/// </summary>
	public class WorkUnitCls : PlanItemCls<WorkTimeCls>, IWorkUnitData
	{
		#region KONSTRUKTORY
		/// <summary>
		/// Konstruktor = pro konkrétního Parenta, kterým je instance paralelního průchodu
		/// </summary>
		/// <param name="parent"></param>
		private WorkUnitCls(WorkTimeCls parent)
			: base(parent)
		{
		}
		/// <summary>
		/// Statický konstruktor
		/// </summary>
		/// <param name="parent">Objekt parent, který představuje společný pracovní čas</param>
		/// <param name="args">Data o využití kapacit</param>
		/// <returns></returns>
		internal static WorkUnitCls Create(WorkTimeCls parent, TaskRegDataAddUnitArgs args)
		{
			WorkUnitCls unit = new WorkUnitCls(parent);
			unit._Category = args.Category;
			unit._CapacitySource = args.CapacitySource;
			unit._CapacityUnit = args.CapacityUnit;
			unit._CapacityLevelTimeIndex = args.CapacityLevel;
			unit._CapacityDesk = args.CapacityDesk;
			unit._UseRatio = args.CapacityUseRatio;
			return unit;
		}
		/// <summary>
		/// Vizualizace
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			return "{Category=" + this.Category + "; Source=" + this.CapacitySource + "; PUC=" + this.CapacityUnit + "}";
		}
		#endregion
		#region PROMĚNNÉ A PROPERTY
		/// <summary> { get; } GID jednotky = klíč z čísla pracovní třídy (Constants.ClassNumberWork = 0x10004) a čísla záznamu WorkID</summary>
		internal GID WorkGID { get { return new GID(Constants.ClassNumberWork, this.ItemID); } }
		/// <summary> { get; } ID Task = operace</summary>
		public int TaskID { get { return this.Parent.TaskID; } }
		/// <summary> { get; } Číslo DB záznamu tasku = operace</summary>
		public int TaskRecordNumber { get { return this.Parent.TaskRecordNumber; } }
		/// <summary> { get; } Data úkolu = operace. Jedna operace může mít 1 i víc průchodů = Pass.</summary>
		public CapacityPlanWorkItemCls TaskData { get { return this.Parent.TaskData; } }
		/// <summary> { get; } ID Axis = materiálová osa</summary>
		public int AxisID { get { return this.Parent.AxisID; } }
		/// <summary> { get; } Data osy S = materiálová osa</summary>
		internal MaterialPlanAxisItemCls AxisData { get { return this.Parent.AxisData; } }
		/// <summary> { get; } ID Pass = jeden z paralelních průběhů</summary>
		public int PassID { get { return this.Parent.PassID; } }
		/// <summary> { get; } ID Time = společný čas jednotka práce</summary>
		public int TimeID { get { return this.Parent.TimeID; } }
		/// <summary> { get; } Jednoznačné ID tohoto záznamu, neměnné, unikátní přes všechny okolní záznamy.</summary>
		public int WorkID { get { return this.ItemID; } }
		/// <summary> { get; } ID kapacitní jednotky = kde se práce koná (PlanUnitC)</summary>
		public int UnitID { get { return this._CapacityUnit; } }
		/// <summary> { get; } Index stolu kapacitní jednotky = kde konkrétně se práce koná</summary>
		public int DeskID { get { return this._CapacityDesk; } }
		/// <summary>
		/// Klíč RecordNumber desky stolu, kde práce probíhá. Nastavuje se po uložení stavu kapacit =» linky kapacit (PlanUnitCDeskCls), 
		/// odsud se čte při ukládání WorkUnitCls do PlanUnitCWorkCls.PlanUnitCDesk
		/// </summary>
		public int DeskRecordNumber { get { return _DeskRecordNumber; } set { _DeskRecordNumber = value; } }
		private int _DeskRecordNumber;
		/// <summary> { get; }
		/// Časový interval, po který se v tomto časovém úseku pracuje.
		/// Jednotlivé úseky Time v rámci jednoho WorkPass na sebe navazují, nepřekrývají se.
		/// Časový úsek WorkTimeCls.Time nemůže přesahovat přes hranu stavu kapacit, z níž čerpá:
		/// pokud čerpáme z více (i těsně sousedních) stavů kapacit, existuje pro každý stav kapacit jeho odpovídající záznam Time.
		/// </summary>
		public TimeRange WorkTime { get { return this.Parent.WorkTime; } }
		/// <summary>
		/// Kapacita, kterou tato práce odebírá ze zadaného pracovního průchodu.
		/// Je to fyzický čas (WorkTime) v sekundách krát koeficient StandardRatio.
		/// </summary>
		public decimal PassCapacity { get { return this.Parent.PassCapacity; } }
		/// <summary> { get; } Druh tasku, určuje cílový druh čerpaných kapacit (Reál, Plán, Simulace)</summary>
		public CapacityUsageType CapacType { get { return this.Parent.CapacType; } }
		///<summary> { get; } Množství finálního výrobku celkem - toto množství se neplánuje, jen slouží k určování potřebnosti alokace fáze MoveTime, TBC, TEC.</summary>
		public decimal QtyFinal { get { return this.Parent.QtyFinal; } }
		///<summary> { get; } Množství chybějící, toto se bere do plánu. Fyzicky vyráběný počet na tomto tasku.</summary>
		public decimal QtyRequired { get { return this.Parent.QtyRequired; } }
		/// <summary> { get; }
		/// Příznak, zda operace je časově fixovaná (true = fixovaná / false = volná)
		/// </summary>
		public bool IsFixedTask { get { return this.Parent.IsFixedTask; } set { this.Parent.IsFixedTask = value; } }
		/// <summary>
		/// Reference na WorkPass = výrobní průchod
		/// </summary>
		internal WorkPassCls WorkPass { get { return this.Parent.Parent; } }
		/// <summary>
		/// Kategorie zdroje (pracoviště, profese, obecný zdroj)
		/// </summary>
		public CapacitySourceCategory Category { get { return this._Category; } }
		private CapacitySourceCategory _Category;
		/// <summary>
		/// Vrací Int32 kategorii zdroje, a pokud není zadán (má hodnotu 0), pak vrátí Int32.MaxValue.
		/// Vhodné pro porovnávání.
		/// </summary>
		private int _CategoryIntMax { get { return ((int)this._Category == 0 ? Int32.MaxValue : (int)this._Category); } }
		/// <summary>
		/// Zdroj kapacit = obecný prostředek výroby
		/// </summary>
		public int CapacitySource { get { return this._CapacitySource; } }
		private int _CapacitySource;
		/// <summary>
		/// Kapacitní plánovací jednotka = konkrétní výrobní prostředek
		/// </summary>
		public int CapacityUnit { get { return this._CapacityUnit; } }
		private int _CapacityUnit;
		/// <summary>
		/// Klíč stavu kapacit TimeIndex = pro rychlé odebrání při odregistraci.
        /// Nejde o LevelID.
		/// </summary>
		public int CapacityLevelTimeIndex { get { return this._CapacityLevelTimeIndex; } }
		private int _CapacityLevelTimeIndex;
		/// <summary>
		/// Index stolu kapacitní plánovací jednotka = konkrétní výrobní prostředek, jeho linka
		/// </summary>
		public int CapacityDesk { get { return this._CapacityDesk; } }
		private int _CapacityDesk;
		/// <summary>
		/// Poměr využití kapacity navázané jednotky, typicky 1 (nikdy ne větší). 
		/// Jiná hodnota je v případě částečného použití kapacity (souběžná výroba více úkolů).
		/// </summary>
		public decimal UseRatio { get { return this._UseRatio; } }
		private decimal _UseRatio;
		/// <summary>
		/// Použitá kapacita = WorkTime krát UseRatio.
		/// Jde o kapacitu z kapacitní jednotky, tedy = kolik práce se spotřebuje (nikoli hodnota, kolik užitku to přinese, to je rozdíl !)
		/// </summary>
		public decimal UseCapacity { get { return (decimal)(this.WorkTime.Seconds) * this.UseRatio; } }
		/// <summary>
		/// Obsahuje DataPointer na tento WorkUnit
		/// </summary>
		public DataPointerStr DataPointer { get { return this._GetDataPointer(); } }
		/// <summary>
		/// Vrátí DataPointer na tento WorkUnit
		/// </summary>
		/// <returns></returns>
		private DataPointerStr _GetDataPointer()
		{
			return new DataPointerStr(
				new GID(PlanUnitCCls.ClassNr, this.UnitID),
				new GID(Constants.ClassNumberWork, this.WorkID));
		}
		#endregion
		#region KOMPARÁTOR PRO TŘÍDĚNÍ
		/// <summary>
		/// Porovná dvě jednotky podle priority.
		/// Nejprve jsou podle kategorie: Workplace, Profession, Resource.
		/// Pak jsou podle CapacityUnit.
		/// Pak jsou podle CapacityDesk.
		/// A pak je to jedno.
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		internal static int CompareBestOf(WorkUnitCls a, WorkUnitCls b)
		{
			int cmp = a._CategoryIntMax.CompareTo(b._CategoryIntMax);
			if (cmp != 0) return cmp;
			cmp = a._CapacityUnit.CompareTo(b._CapacityUnit);
			if (cmp != 0) return cmp;
			cmp = a._CapacityDesk.CompareTo(b._CapacityDesk);
			return cmp;
		}
		#endregion
		#region LOAD & SAVE Z/DO DATABÁZE
		/// <summary>
		/// Metoda vytvoří, naplní a vrátí data o jednom WorkPass na základě dat načtených z databáze.
		/// </summary>
		/// <param name="record">Záznam s daty, načtený z databáze</param>
		/// <param name="timeItem">Zaznam Parent</param>
		/// <returns></returns>
		internal static WorkUnitCls LoadFromDb(PlanUnitCUnitCls record, WorkTimeCls timeItem, CapacityDeskCls deskItem)
		{
			WorkUnitCls unit = new WorkUnitCls(timeItem);
			unit.RecordNumber = record.RecordNumber;               // číslo nonsubjektu uložím do dat v plánovacím procesu
			unit._Category = PlanningComponentCls.SqlInt16ToSourceCategory(record.Category);
			unit._CapacitySource = PlanningComponentCls.RelationToInt32(record.CapacitySource);
			unit._CapacityUnit = deskItem.PlanUnitC;
			unit._CapacityLevelTimeIndex = deskItem.CapacityLevel.TimeIndex;
			unit._CapacityDesk = deskItem.DeskIndex;
			unit._UseRatio = (record.UseRatio.IsNull ? 0M : record.UseRatio.Value);
			return unit;
		}
		/// <summary>
		/// Metoda uloží veškerá svoje data do záznamu PlanUnitCUnitCls a jeho prostřednictvím do databáze.
		/// Podřízené třídy nemá...
		/// </summary>
		internal override void SaveRecord()
		{
			this._SaveRecordUnit();
		}
		/// <summary>
		/// Metoda uloží veškerá svoje data do záznamu PlanUnitCUnitCls  a jeho prostřednictvím do databáze.
		/// Neukládá podřízené třídy.
		/// </summary>
		private void _SaveRecordUnit()
		{
			using (PlanUnitCUnitCls record = new PlanUnitCUnitCls())
			{
				record.Category = PlanningComponentCls.SourceCategoryToSqlInt16(this.Category) ;
				record.UseRatio = this.UseRatio;
				record.PlanUnitCTime = this.Parent.RecordNumber;
				record.PlanUnitCDesk = this.DeskRecordNumber;
				record.CapacitySource = PlanningComponentCls.Int32ToRelation(this.CapacitySource);

				this.SaveViaRecord(record);
			}
		}
		#endregion
		#region UMÍSTĚNÍ ELEMENTU (ZÍSKÁNÍ RELATIVNÍ POZICE TOHOTO ELEMENTU V RÁMCI JEHO WorkPass)
		/// <summary> { get; }
		/// Pozice relativního umístění tohoto elementu v rámci jeho WorkPass (jeden průchod).
		/// Získání lokátoru chvilku trvá, musí se projít skrz všechny WorkTime aktuálního WorkPass a najít aktuální relativní čas.
		/// </summary>
		/// <returns></returns>
		internal WorkUnitLocationCls WorkUnitLocation
		{
			get
			{
				return this.WorkPass.WorkUnitLocationGet(this.TaskData.OperationKey, this.Category, this.CapacitySource, this.CapacityUnit, this.CapacityDesk, this.TimeID);
			}
		}
		#endregion
        #region DIAGNOSTIKA
        /// <summary>
        /// Souhrn klíčů (ID): this
        /// </summary>
        internal string DebugKeys
        {
            get
            {
                string text = "Unit " + this.WorkID.ToString();
                return text;
            }
        }
        /// <summary>
        /// Přidá svoje data do diagnostiky (pár věcí z hlavičky, jednotky)
        /// </summary>
        /// <param name="planningDataCls"></param>
        /// <param name="rowPrefix"></param>
        internal void AddToDiagnostic(PlanningDataCls planningDataCls, string title, string rowPrefix)
        {
            Steward.Diagnostic.SubTitleAdd(title);
            string pref = (rowPrefix == null ? "" : rowPrefix);
            string text = "WORK ID=" + this.WorkID.ToString() +
                "; Category=" + this.Category.ToString() +
                "; Source (" + this.CapacitySource.ToString() + ")=" + planningDataCls.GetReferNazev(Constants.ClassNumberCapacitySource, this.CapacitySource) +
                "; CUnit (" + this.CapacityUnit.ToString() + ")=" + planningDataCls.GetReferNazev(PlanUnitCCls.ClassNr, this.CapacityUnit);
            Steward.Diagnostic.InfoAddLine(pref + text);
        }
        #endregion
    }
	#endregion
	#region CLASS WorkUnitLocationCls : souhrn dat o relativním umístění elementu v prostoru WorkPassCls.
	/// <summary>
	/// WorkUnitLocationCls : souhrn dat o relativním umístění elementu v prostoru WorkPassCls.
	/// Toto relativní umístění lze získat z instanční property WorkUnitLocation přímo na objektu třídy WorkUnitCls,
	/// anebo statickou metodou WorkPassCls.GetWorkUnitLocation(WorkUnitCls unit).
	/// Tento lokátor je třeba uschovat, a následně po přeregistraci kapacity lze získat konkrétní element, 
	/// který nejblíže odpovídá umístění původního elementu,
	/// a to metodou FindUnitItem(locator) na instanci třídy tasku (CapacityPlanWorkItemCls).
	/// </summary>
	internal class WorkUnitLocationCls
	{
		/// <summary>
		/// Konstruktor lokátoru, výhradně s plnými daty. Lokátor je konstantní.
		/// </summary>
		/// <param name="taskKey">Klíč tasku</param>
		/// <param name="passKey">Klíč paralelního průchodu</param>
		/// <param name="operationKey">Klíč operace</param>
		/// <param name="capacityDesk">Klíč kapacitní linky (stolu)</param>
		/// <param name="phase">Typ fáze</param>
		/// <param name="category">Kategorie zdroje kapacit</param>
		/// <param name="capacitySource">Obecný zdroj kapacit</param>
		/// <param name="capacityUnit">Kapacitní jednotka</param>
		/// <param name="phaseTimeTotal">Celková kapacita fáze</param>
		/// <param name="elementTimeBegin">Počátek času kapacity v čase elementu</param>
		/// <param name="elementTimeEnd">Konec času kapacity v čase elementu</param>
		internal WorkUnitLocationCls(int taskKey, int passKey, int operationKey, OperationPhase phase, CapacitySourceCategory category, int capacitySource, int capacityUnit, int capacityDesk, decimal phaseTimeTotal, decimal elementTimeBegin, decimal elementTimeEnd)
		{
			this.TaskKey = taskKey;
			this.PassKey = passKey;
			this.OperationKey = operationKey;
			this.Phase = phase;
			this.Category = category;
			this.CapacitySource = capacitySource;
			this.CapacityUnit = capacityUnit;
			this.CapacityDesk = capacityDesk;

			this.PhaseTimeRatioBegin = 0M;
			this.PhaseTimeRatioEnd = 0M;
			if (phaseTimeTotal > 0M &&
				elementTimeBegin >= 0M && elementTimeBegin <= phaseTimeTotal &&
				elementTimeEnd >= elementTimeBegin && elementTimeBegin <= phaseTimeTotal)
			{
				this.PhaseTimeRatioBegin = elementTimeBegin / phaseTimeTotal;
				this.PhaseTimeRatioEnd = elementTimeEnd / phaseTimeTotal;
			}
		}
		/// <summary>
		/// Vizualizace
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			return "{" + this.Category.ToString() + "=" + this.CapacitySource.ToString() + "; Unit=" + this.CapacityUnit.ToString() +
				"; Op.=" + this.OperationKey.ToString() + "; Phase=" + this.Phase.ToString() +
				"; Position=«" + this.PhaseTimeRatioBegin.ToString("0.000") + " ÷ " + this.PhaseTimeRatioEnd.ToString("0.000") + "»}";
		}
		/// <summary>
		/// Aktuální klíč tasku, na který se lokátor odkazuje
		/// </summary>
		internal int TaskKey { get; private set; }
		/// <summary>
		/// Klíč paralelního průchodu, na který se lokátor odkazuje
		/// </summary>
		internal int PassKey { get; private set; }
		/// <summary>
		/// Aktuální klíč operace, která je prováděná v čase elementu
		/// </summary>
		internal int OperationKey { get; private set; }
		/// <summary>
		/// Aktuální fáze, která je prováděná v čase elementu
		/// </summary>
		internal OperationPhase Phase { get; private set; }
		/// <summary>
		/// Kategorie zdroje (pracoviště, profese, obecný zdroj)
		/// </summary>
		internal CapacitySourceCategory Category { get; private set; }
		/// <summary>
		/// Zdroj kapacit = obecný prostředek výroby
		/// </summary>
		internal int CapacitySource { get; private set; }
		/// <summary>
		/// Kapacitní plánovací jednotka = konkrétní výrobní prostředek
		/// </summary>
		internal int CapacityUnit { get; private set; }
		/// <summary>
		/// Kapacitní linka = stůl = pořadové číslo stolu (počínaje 0, index)
		/// </summary>
		internal int CapacityDesk { get; private set; }
		/// <summary>
		/// Relativní počátek času daného elementem vzhledem k celkovému času dané fáze (v rozsahu 0 ÷this.PhaseTimeRatioEnd)
		/// </summary>
		internal decimal PhaseTimeRatioBegin { get; private set; }
		/// <summary>
		/// Relativní konec času daného elementem vzhledem k celkovému času dané fáze (v rozsahu this.PhaseTimeRatioBegin ÷ 1)
		/// </summary>
		internal decimal PhaseTimeRatioEnd { get; private set; }
	}
	#endregion
}
